题目描述
在一个圆形操场的四周摆放N堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。
试设计出1个算法,计算出将N堆石子合并成1堆的最小得分和最大得分.
输入输出格式
输入格式:
数据的第1行试正整数N,1≤N≤100,表示有N堆石子.第2行有N个数,分别表示每堆石子的个数.
输出格式:
输出共2行,第1行为最小得分,第2行为最大得分.
输入输出样例
输入样例#1:
4 4 5 9 4
输出样例#1:
43 54
这道题目...开始本来都已经想到了利用一个区间变量然后从间隔为1一直计算到间隔为n-1...结果还是没有做出来...
原因是 状态转移方程没写好...我最初的写法是dp[i][j] = max(dp[i][j-1]+sz[j],dp[i+1][j]+sum[i])...真是失败...唉....
这道题目很容易就能想到以间隔从小到大一层一层的来计算, 但是我没有抓好状态变化导致状态转移方程错误
因为在间隔为k的时候有很多中情况,我可以挑选一个间隔为1另一个间隔为k-1 也可以挑选间隔为2另一个间隔为k-2, 所以这些都要遍历到, 所以在确定了区间i到j的情况下, 我们还需要从中找出和最大或者最小的那个区间,所以额外再用一个循环来遍历.
因为是环形数组, 但是因为最大间隔为n-1, 实际并不能遍历到n*2以外的地方, 所以可以开一个两倍大的数组,用直线模拟环
另一个点就是sum数组, 它是一个前缀和数组, sum[i]表示从1到i的元素的和, 这样要求区间i - j的和可以表示为sum[j] - sum[i-1].
最后一个点就是在递推遍历的时候i要遍历到2*n.
我之前用的是i<=n然后一直错, 到后面顶点查错的时候才发现, 在区间4-6区域, dp[4][6] = dp[4][4] + dp[5][6]+ sum[6]-sum[3].
其中因为点只遍历到n, 从而dp[5][6]的值并没有计算出来,所以导致结果错误.
妙啊
另外还是
I HATE DP
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
int sum[105];//前缀和
int sz[205];
int mindp[205][205],maxdp[205][205];
int n;
int main(){
int tmp;
cin>>n;
memset(mindp,0,sizeof(mindp));
memset(maxdp,0,sizeof(maxdp));
sum[0] = 0;
for(int i=1;i<=n;i++){
cin>>tmp;
sz[i] = sz[n+i] = tmp;
}
for(int i=1;i<=n*2;i++){
sum[i] = sum[i-1]+sz[i];//sum是没有错误的
}
for(int j=1;j<n;j++){
for(int i=1;i<=2*n;i++){
int tp = i+j;
mindp[i][tp]=0x3f3f3f3f;
for(int k=i;k<tp&&k<=2*n;k++){
maxdp[i][tp]=max(maxdp[i][tp],maxdp[i][k]+maxdp[k+1][tp]+sum[tp]-sum[i-1]);
mindp[i][tp]=min(mindp[i][tp],mindp[i][k]+mindp[k+1][tp]+sum[tp]-sum[i-1]);
// if(i == 4 && tp == 6){
// cout<<"k: "<<mindp[i][k]<<" "<<mindp[k+1][tp]<<endl;
// cout<<sum[tp]<<" "<<sum[i-1]<<" "<<mindp[i][tp]<<endl;
// cout<<"********"<<endl;
// }
//错误原因 在计算4 - 6 这个区间内的结果时会遇到dp[4][4] + dp[5][6] 然而此时dp[5][6]并没有赋值
}
}
}
//测试
// for(int j=1;j<n;j++){
// cout<<"j="<<j<<" : ";
// for(int i=1;i<=n;i++){
// int tp = i+j;
// cout<<mindp[i][tp]<<" ";
// }
// cout<<endl;
// }
//
int mi=0x3f3f3f3f,ma=0;
for(int i=1;i<=n;i++){
// cout<<mindp[i][i+n-1]<<" ";
mi = min(mi,mindp[i][i+n-1]);
ma = max(ma,maxdp[i][i+n-1]);
}
// cout<<endl;
// for(int i=1;i<=n;i++){
// cout<<maxdp[i][i+n-1]<<" ";
// }
// cout<<endl;
cout<<mi<<"\n"<<ma<<endl;
return 0;
}