区间DP——石子合并一家人(C++)

 

 

A. 石子合并<一>

入门版:一线

内存限制:128 MiB    时间限制:1000 ms    标准输入输出
题目类型:传统    评测方式:文本比较
题目描述

有N堆石子排成一排(n<=100),现要将石子有次序地合并成一堆,规定每次只能选相邻的两堆合并成一堆,并将新的一堆的石子数,记为改次合并的得分,编一程序,由文件读入堆数n及每堆石子数(<=200);

(1)选择一种合并石子的方案,使得做n-1次合并,得分的总和最少

(2)选择一种合并石子的方案,使得做n-1次合并,得分的总和最多

输入格式

第一行为石子堆数n 第二行为每堆石子数,每两个数之间用一空格分隔。

输出格式

从第1行为得分最小第二行是得分最大。

样例

样例输入
4
4 5 9 4

样例输出
44
54

 首先,本文讲的是DP石子合并,而非贪心合并果子二者的区别就在于此——每次只能选相邻的两堆合并成一堆还是任意合并。

思路

设dpmin[i]--[j]表示

附上代码

 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 using namespace std;
 5 int n,dpmax[500][500],dpmin[500][500]/*前i-j最优解*/,sum[500][500],read[500],tot;
 6 int main()
 7 {
 8     cin>>n;
 9     for(int i=1;i<=n;i++)
10     {
11         cin>>read[i];
12     }
13     for(int i=1;i<=n;i++)
14     {
15         tot=read[i];
16         for(int j=i+1;j<=n;j++)
17         {
18             tot+=read[j];
19             sum[i][j]=tot;
20         }
21     }
22     memset(dpmin,0x3f,sizeof(dpmin));
23     for(int i=1;i<=n;i++)
24         dpmin[i][i]=0; 
25     for(int len=1;len<n;len++)
26     {
27         for(int i=1;i<=n&&len+1<=n;i++)
28         {
29             int j=len+i;
30             for(int k=i;k<=j;k++)
31             {
32                 dpmax[i][j]=max(dpmax[i][j],dpmax[i][k]+dpmax[k+1][j]);
33                 dpmin[i][j]=min(dpmin[i][j],dpmin[i][k]+dpmin[k+1][j]);
34             }
35             dpmax[i][j]+=sum[i][j];
36             dpmin[i][j]+=sum[i][j]; 
37         }
38     }
39     cout<<dpmin[1][n]<<endl;
40     cout<<dpmax[1][n];
41 }

 

 

B. 石子合并<2>

进阶版:拆分

题目描述

在一个园形操场的四周摆放N堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。 试设计出1个算法,计算出将N堆石子合并成1堆的最小得分和最大得分.

输入格式

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

输出格式

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

样例
样例输入
4
4 4 5 9
样例输出

43

54

 

 

 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 using namespace std;
 5 int n,dpmax[1001][1001],dpmin[1001][1001],sum[1001][1001],read[1001],tot,maxi,mini=99999999;
 6 int main()
 7 {
 8     cin>>n;
 9     for(int i=1;i<=n;i++)
10         cin>>read[i];
11     for(int i=n+1;i<=2*n-1;i++)
12         read[i]=read[i-n];
13     for(int i=1;i<=2*n-1;i++)
14     {
15         tot=read[i];
16         for(int j=i+1;j<=2*n-1;j++)
17         {
18             tot+=read[j];
19             sum[i][j]=tot;
20         }
21     }
22     memset(dpmin,0x3f,sizeof(dpmin));
23     for(int i=1;i<=2*n-1;i++)
24         dpmin[i][i]=0; 
25     for(int len=1;len<n;len++)
26     {
27         for(int i=1;i<=2*n-1&&len+i<=2*n-1;i++)
28         {
29             int j=len+i;
30             for(int k=i;k<=j;k++)
31             {
32                 dpmax[i][j]=max(dpmax[i][j],dpmax[i][k]+dpmax[k+1][j]);
33                 dpmin[i][j]=min(dpmin[i][j],dpmin[i][k]+dpmin[k+1][j]);
34             }
35             dpmax[i][j]+=sum[i][j];
36             dpmin[i][j]+=sum[i][j];
37         }
38     }
39     for(int i=1;i<=n;i++)
40     {
41         maxi=max(maxi,dpmax[i][i+n-1]);
42         mini=min(mini,dpmin[i][i+n-1]);
43     }
44     cout<<mini<<endl<<maxi;
45 }

 

 

C. 石子合并<3>

优化版:数学支持

 

题目描述

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

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

输入格式

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

输出格式

输出共1行,最大得分

样例
样例输入
4
4 4 5 9
样例输出
54


 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 using namespace std;
 5 unsigned int n,dpmax[4011][4011],sum[4011][4011],read[4011],tot,maxi,mini=99999999,k;
 6 int main()
 7 {
 8     cin>>n;
 9     for(int i=1;i<=n;i++)
10         cin>>read[i];
11     for(int i=n+1;i<=2*n-1;i++)
12         read[i]=read[i-n];
13     for(int i=1;i<=2*n-1;i++)
14     {
15         tot=read[i];
16         for(int j=i+1;j<=2*n-1;j++)
17         {
18             tot+=read[j];
19             sum[i][j]=tot;
20         }
21     }
22     for(int len=1;len<n;len++)
23     {
24         for(int i=1;i<=2*n-1&&len+i<=2*n-1;i++)
25         {
26             int j=len+i;
27             dpmax[i][j]=max(dpmax[i][j-1],dpmax[i+1][j]);
28             dpmax[i][j]+=sum[i][j];
29         }
30     }
31     for(int i=1;i<=n;i++)
32         maxi=max(maxi,dpmax[i][i+n-1]);
33     cout<<maxi;
34 }

 

 

转载于:https://www.cnblogs.com/lihaolin/p/11270310.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值