石子归并有三个经典问题:
https://blog.csdn.net/acdreamers/article/details/18039073
//刚找到的大佬blog
1.洛谷 P1880[NOI1995]石子合并 (环形版)
题目描述
在一个圆形操场的四周摆放N堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,
记为该次合并的得分。试设计出1个算法,计算出将N堆石子合并成1堆的最小得分和最大得分.
https://wenku.baidu.com/view/7da2fccf0508763231121213.html?rec_flag=default&sxts=1565666284413
//讲述了 为什么 环形版 不是 贪心 的原因
1 #include<iostream> 2 3 using namespace std; 4 5 int n,a[205],sum[205],cnt[205][205],ans[205][205],dp[205][205]; 6 7 int main(){ 8 cin>>n; 9 for(int i=0;i<n;i++){ 10 cin>>a[i]; 11 a[i+n]=a[i]; 12 sum[i]=sum[i-1]+a[i]; 13 dp[i][i]=i; 14 } 15 for(int i=n;i<n*2;i++){ 16 sum[i]=sum[i-1]+a[i]; 17 dp[i][i]=i; 18 } 19 for(int i=n*2-1;i;i--){ 20 for(int j=i+1;j<n*2;j++){ 21 int jc=0,temp=0x3f3f3f3f; 22 ans[i][j]=max(ans[i][j-1],ans[i+1][j])+sum[j]-sum[i-1]; 23 // 不能用四边形不等式 不满足 单调性 24 for(int k=dp[i][j-1];k<=dp[i+1][j];k++){ 25 int t=cnt[i][k]+cnt[k+1][j]+(sum[j]-sum[i-1]); 26 if(t<temp){ 27 temp=t; 28 jc=k; 29 } 30 } 31 dp[i][j]=jc; 32 cnt[i][j]=temp; 33 } 34 } 35 int maxn=0,minn=0x3f3f3f3f; 36 for(int i=1;i<=n;i++){ 37 maxn=max(maxn,ans[i][i+n-1]); 38 minn=min(minn,cnt[i][i+n-1]); 39 } 40 cout<<minn<<endl<<maxn; 41 return 0; 42 }
2.CSU - 1592 I-石子归并(直线版) http://acm.csu.edu.cn:20080/csuoj/problemset/problem?pid=1592 题目描述 现在有n堆石子,第i堆有ai个石子。现在要把这些石子合并成一堆,每次只能合并相邻两个,每次合并的代价是两堆石子的总石子数。 求合并所有石子的最小代价。
(直线版)题解分析
重点词:只能合并相邻两个 ———————— 就是区间dp
区间dp就是在区间上的动态规划,求解一段区间上的最优解,通过合并小区间的最优解来得到整个大区间上的最优解的算法。
区间dp一般都是三层for循环 需要注意的是 区间是从小到大 因为dp是后一个用到前一个的给出的结果 并进行递推
区间dp常用的一个状态就是dp[i][j]表示i~j这个区间的最优值是多少
区间dp的大致思路:
1.确定状态 初始化长度为 n 的dp[][]的值
2.枚举区间长度 枚举区间的起始点 (有些题还需枚举断点) 由小区间转移到大区间
3.最终答案基本是 dp[1][n] or dp[0][n-1]
优化:
四边形不等式优化
https://blog.csdn.net/noiau/article/details/72514812 //大佬讲解 侵删
对于(a<b<=c<d) 如果存在 f[a][c]+f[b][d]<=f[b][c]+f[a][d] 则说这个东西满足四边形不等式
(可以理解为一句话,交叉小于包含,即交叉的两个区间,a到c和b到d的值满足小于等于包含的两个区间[bc包含于ad])
当然这个东西可能是dp数组,也可以是其他数组,比如引入里提到的cost数组,表示的是 i到j的花费(比如合并石子问题)
给出两个定理:
1、如果上述的w函数同时满足区间包含单调性和四边形不等式性质,那么函数dp也满足四边形不等式性质
我们再定义s(i,j)表示 dp(i,j) 取得最优值时对应的下标(即 i≤k≤j 时,k 处的 dp 值最大,
则 s(i,j)=k此时有如下定理
2、假如dp(i,j)满足四边形不等式,那么s(i,j)单调,即 s(i,j)≤s(i,j+1)≤s(i+1,j+1)
1 #include<iostream> 2 #define range 100010 3 using namespace std; 4 5 int dp[105][105],s[105][105],a[105],cnt[105]; 6 int t,n; 7 8 int main(){ 9 cin>>t; 10 while(t--){ 11 cin>>n>>a[0]; 12 cnt[0]=a[0]; 13 for(int i=1;i<n;i++){ 14 cin>>a[i]; 15 cnt[i]=cnt[i-1]+a[i]; 16 } 17 for(int i=0;i<n;i++){ 18 dp[i][i]=0; 19 s[i][i]=i; 20 } 21 for(int i=n-1;i>=0;i--){ 22 for(int j=i+1;j<n;j++){ 23 int temp=0x7fffffff; 24 int te; 25 for(int k=s[i][j-1];k<=s[i+1][j];k++){ 26 if(temp>dp[i][k]+dp[k+1][j]+cnt[j]-cnt[i-1]){ 27 temp=dp[i][k]+dp[k+1][j]+cnt[j]-cnt[i-1]; 28 te=k; 29 } 30 } 31 dp[i][j]=temp; 32 s[i][j]=te; 33 } 34 } 35 cout<<dp[0][n-1]<<endl; 36 } 37 }
3.类似洛谷 P1090合并果子(任意版) 贪心