码迷,mamicode.com
首页 > 其他好文 > 详细

[LuoGu P1880 ] 石子合并

时间:2017-11-06 21:23:33      阅读:152      评论:0      收藏:0      [点我收藏+]

标签:font   输入   std   logs   cstring   记忆   display   逆时针   alt   

题目大意:

在一个圆形操场的四周摆放N堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。

试设计出1个算法,计算出将N堆石子合并成1堆的最小得分和最大得分.

 

输入输出格式:

输入格式: 

数据的第1行试正整数N,1≤N≤100,表示有N堆石子.第2行有N个数,分别表示每堆石子的个数.

输出格式:


输出共2行,第1行为最小得分,第2行为最大得分.

 

由于本人是大蒟蒻,所以这道题做了好几个月(其实第一眼看就想到断环成链,但是蒟蒻是完美主义者,所以铁了心要写DP)

断环成链的就不说了,点开LuoGu 题解一大堆.

这里说一下我的DP思路:

若果是一条链,那么这个问题是很好处理的,有状态转移方程(以最小值为例) : f[i][j]=min{ f[i][k]+f[k+1][j]+sum(i,j) } | i<=k<=j.

那么成环之后也可以用类似的状态: 假设石子的序号逆时针递增,则f[i][j] 表示以逆时针方向最优合并,那么f[i][j]|0<=i<=n-1,0<=j<=n-1 就足以表示所有的情况(lrj:想一想,为什么 =‘-‘) )

蒟蒻觉得用记忆化搜索比用递推好(其实是因为递推的话蒟蒻不知道顺序),那么,请看代码:

 

技术分享
 1 //Copyright(C)Sunshine.
 2 //2017.11.06
 3 #include<cstdio>
 4 #include<cstring>
 5 const int N=100+1,INF=2e9+1;
 6 int a[N],sum[N],MinS[N][N],MaxS[N][N],n;
 7 bool calMin[N][N],calMax[N][N];
 8 int min(int a,int b){return a<b?a:b;}
 9 int max(int a,int b){return a>b?a:b;}
10 inline int SUM(int,int);
11 inline int DP(int,int,int val[N][N],bool vis[][N],int(*)(int,int));
12 inline void init();
13 inline void dp();
14 inline void PrintAnswer();
15 int main()
16 {
17     init();
18     dp();
19     PrintAnswer();
20     return 0;
21 }
22 inline int SUM(int l,int r)
23 {
24     if(l==r)return a[l];
25     if(l<r)return ( sum[r]-sum[l]+a[l] );
26     return (sum[n-1]-sum[l]+a[l])+sum[r];
27 }
28 inline void init()
29 {
30     memset(MinS,0x7f,sizeof(MinS));
31     scanf("%d",&n);
32     for(int i=0;i<n;i++)
33     {
34         scanf("%d",&a[i]);
35         sum[i]=sum[i-1]+a[i];
36     }
37 }
38 inline void dp()
39 {
40     for(int i=0;i<=n-1;i++)
41         for(int j=0;j<=n-1;j++)
42         {
43             if(!calMin[i][j])MinS[i][j]=DP(i,j,MinS,calMin,min);
44             if(!calMax[i][j])MaxS[i][j]=DP(i,j,MaxS,calMax,max);
45         }    
46 }
47 inline int DP(int from,int to,int val[][N],bool vis[][N],int (*f)(int,int))
48 {
49     if(vis[from][to])return val[from][to];
50     vis[from][to]=true;
51     int ans=(-1)*f(INF,-INF);
52     if(from<to)
53     {
54         if(from==to-1)return val[from][to]=SUM(from,to);
55         for(int k=from;k<to;k++)
56         {
57             int m1=DP(from,k,val,vis,f),m2=DP((k+1)%n,to,val,vis,f);
58             ans=f(ans,m1+m2+SUM(from,to) );
59         }
60         return val[from][to]=ans;
61     }
62     else if(from==to){return val[from][to]=0;}
63     else
64     {
65         if((from+1)%n==to)return val[from][to]=SUM(from,to);
66         for(int k=(from)%n;k!=to;k=(k+1)%n)
67         {
68             int m1=DP(from,k,val,vis,f),m2=DP((k+1)%n,to,val,vis,f);
69             ans=f(ans,m1+m2+SUM(from,to));
70         }
71         return val[from][to]=ans;
72     }
73 }
74 inline void PrintAnswer()
75 {
76     int MinA=1e8+1,MaxA=-1,l[2],r[2];
77     for(int i=0;i<n;i++)
78     {
79         MinA=min(MinA,MinS[i][(i+n-1)%n]);
80         MaxA=max(MaxA,MaxS[i][(i+n-1)%n]);
81     }
82     printf("%d\n%d",MinA,MaxA);
83 }
StoneUnion

 

[LuoGu P1880 ] 石子合并

标签:font   输入   std   logs   cstring   记忆   display   逆时针   alt   

原文地址:http://www.cnblogs.com/zhinv/p/7794865.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!