区间dp 经典问题 石子归并

石子归并有三个经典问题:

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合并果子(任意版)  贪心

 

转载于:https://www.cnblogs.com/jjjjjjy/p/11344845.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值