动态规划—石子合并(直线和环)

先来看直线的:


N堆石子摆成一条线。现要将石子有次序地合并成一堆。规定每次只能选相邻的2堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的代价。计算将N堆石子合并成一堆的最小代价。

例如: 1 2 3 4,有不少合并方法

1 2 3 4 => 3 3 4(3) => 6 4(9) => 10(19)

1 2 3 4 => 1 5 4(5) => 1 9(14) => 10(24)

1 2 3 4 => 1 2 7(7) => 3 7(10) => 10(20)

括号里面为总代价可以看出,第一种方法的代价最低,现在给出n堆石子的数量,计算最小合并代价。

Input

第1行:N(2 <= N <= 100) 第2 - N + 1:N堆石子的数量(1 <= Aii <= 10000)

Output

输出最小合并代价

Sample Input

4
1
2
3
4

Sample Output

19


虽然知道这是一道dp题目,但是刚开始的时候怎么也想不出怎么做状态转移,于是又是blog和csdn一顿搜,终于搞出来了。

我们设dp[i][j]是从第i堆石子到第j堆石子合并的最小代价,sum[i]是前i堆石子的数量之和,那么状态转移方程就是:

dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1])

就是把第i堆到第j堆石子分成第i堆到第k堆石子、第k+1堆到第j堆石子,再加上这两大堆石子合并(sum[j]-sum[i-1]),在i<=k<j的范围遍历,找到最小值即可。

由于d[i][j]依赖于d[i][k]和d[k+1][j],所以我们要根据len(j-i)的大小从小到大来循环,这样才能每次都能确保d[i][j]分解的d[i][k]和d[k+1][j]在这之前计算过。

具体细节看代码:

 1 #include<iostream>
 2 #include<cstring>
 3 using namespace std;
 4 #define inf 0x3f3f3f3f
 5 const int maxn=101;
 6 int n,a[maxn],dp[maxn][maxn],sum[maxn];
 7 int main()
 8 {
 9   while(cin>>n)
10   {
11
12     for(int i=1;i<=n;i++)
13     {
14       cin>>a[i];
15       sum[i]=sum[i-1]+a[i];
16     }
17     for(int i=1;i<=n;i++)
18     {
19       for(int j=1;j<=n;j++)
20       {
21         dp[i][j]=i==j?0:inf;
22       }
23     }
24     for(int len=1;len<n;len++)
25     {
26       for(int i=1;i+len<=n;i++)
27       {
28         int j=i+len;
29         for(int k=i;k<j;k++)
30         {
31           dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1]);
32         }
33       }
34     }
35     cout<<dp[1][n]<<endl;
36   }
37 }

如果石堆摆成环状呢?

思路就是把它拆成直线型的,比如说题目的1234,我们把它拆成线性的以后就是1234123,然后在从这里找出合并相邻的4堆石子的最小代价就行了,做法跟原来的差不多,不过最后再搜一个最小值,洛谷1880就有这道题,不过洛谷的题还要求最大代价,这是我a了洛谷的代码:

 1 #include<iostream>
 2 #include<cstring>
 3 #define inf 0x3f3f3f3f
 4 using namespace std;
 5 const int maxn=2e3+5;
 6 int n,a[maxn],dp_min[maxn][1001],dp_max[maxn][1001],sum[maxn],s=inf;
 7 int main()
 8 {
 9   cin>>n;
10   for(int i=1;i<=n;i++)
11   {
12     cin>>a[i];
13     sum[i]=sum[i-1]+a[i];
14   }
15   for(int i=n+1;i<=n*2-1;i++)
16   {
17     a[i]=a[i-n];
18     sum[i]=sum[i-1]+a[i];
19   }
20   for(int len=1;len<n;len++)
21   {
22     for(int i=1;i+len<=2*n;i++)
23     {
24       int j=i+len;
25       dp_min[i][j]=inf;
26       for(int k=i;k<j;k++)
27       {
28         dp_min[i][j]=min(dp_min[i][j],dp_min[i][k]+dp_min[k+1][j]+sum[j]-sum[i-1]);
29         dp_max[i][j]=max(dp_max[i][j],dp_max[i][k]+dp_max[k+1][j]+sum[j]-sum[i-1]);
30       }
31     }
32   }
33   int ans_min=inf,ans_max=0;
34   for(int i=1;i<=n;i++)
35   {
36     ans_min=min(ans_min,dp_min[i][i+n-1]);
37     ans_max=max(ans_max,dp_max[i][i+n-1]);
38   }
39   cout<<ans_min<<endl<<ans_max<<endl;
40 }

为什么跑到洛谷去a题了?因为这个做法在这道题会T啊<(_ _)>,O(n^3)的算法,n又是1000,难顶。

不过还好,既然有这道题,一定就有解法。在网上查了一通,说是要用什么四边形不等式优化成O(n^2),看的我真的脑壳疼。

不学会怎么行?不过我就算懂了一些,也表达不出来,给你们推荐这个大佬的blog:https://www.cnblogs.com/cglongge/p/9451161.html

原文地址:https://www.cnblogs.com/hailin545/p/12264451.html

时间: 2024-10-08 12:20:13

动态规划—石子合并(直线和环)的相关文章

合并类动态规划——石子合并

题目描述 Description 在一个园形操场的四周摆放N堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分.试设计出1个算法,计算出将N堆石子合并成1堆的最小得分和最大得分. 输入输出格式 Input/output 输入格式:数据的第1行试正整数N,1≤N≤100,表示有N堆石子.第2行有N个数,分别表示每堆石子的个数.输出格式:输出共2行,第1行为最小得分,第2行为最大得分. 输入输出样例 Sample input/out

石子合并问题(直线版)

首先来个题目链接:http://acm.nyist.net/JudgeOnline/problem.php?pid=737 有个更难的版本(不过很好玩):http://www.lydsy.com/JudgeOnline/problem.php?id=3229 题目: 石子合并(一) 时间限制:1000 ms  |  内存限制:65535 KB 难度:3 描述     有N堆石子排成一排,每堆石子有一定的数量.现要将N堆石子并成为一堆.合并的过程只能每次将相邻的两堆石子堆成一堆,每次合并花费的代价

HRBUST 1818 石子合并问题--直线版

石子合并问题--直线版 Time Limit: 1000ms Memory Limit: 32768KB This problem will be judged on HRBUST. Original ID: 1818 64-bit integer IO format: %lld      Java class name: Main 一条直线上摆放着一行共n堆的石子.现要将石子有序地合并成一堆.规定每次只能选相邻的两堆合并成新的一堆,并将新的一堆石子数记为该次合并的得分.请编辑计算出将n堆石子合

算法温习动态规划之石子合并问题

石子合并问题分为直线型和圆形: 直线型: 直线型狮子合并问题存在以下递推式: f[i][j]:表示从第i堆合并到底j堆,最少代价 f[i][j]=0;     i=j f[i][j]=min( f[i][k]+f[k+1][j]+sum(i,j));       i<=k<j; 这个问题比较好理解,根据递推式,我们的i要从高到底遍历,j要从低到高遍历 直线型代码如下: #include<iostream> #include<vector> using namespace

动态规划思想:石子合并问题

描述:在一个圆形操场的四周摆放着n 堆石子.现要将石子有次序地合并成一堆.规定每次只能选相邻的2 堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的得分.试设计一个算法,计算出将n堆石子合并成一堆的最小得分和最大得分. 贪心算法不能得到最优解,可惜了.首先我们可以把这么堆石子看成一列 如果N-1次合并的全局最优解包含了每一次合并的子问题的最优解,那么经这样的N-1次合并后的得分总和必然是最优的.因此我们需要通过动态规划算法来求出最优解. 在此我们假设有n堆石子,一字排开,合并相邻两堆的石子,

直线石子合并(区间DP)

石子合并 时间限制:1000 ms  |  内存限制:65535 KB 描述有N堆石子排成一排,每堆石子有一定的数量.现要将N堆石子并成为一堆.合并的过程只能每次将相邻的两堆石子堆成一堆,每次合并花费的代价为这两堆石子的和,经过N-1次合并后成为一堆.求出总的代价最小值和最大值. 输入有多组测试数据,输入到文件结束.每组测试数据第一行有一个整数n,表示有n堆石子.接下来的一行有n(0< n <200)个数,分别表示这n堆石子的数目,用空格隔开 输出输出总代价的最小值以及最大值(中间以空格隔开)

zjnu1181 石子合并【基础算法?动态规划】——高级

Description 在操场上沿一直线排列着 n堆石子.现要将石子有次序地合并成一堆.规定每次只能选相邻的两堆石子合并成新的一堆, 并将新的一堆石子数记为该次合并的得分.允许在第一次合并前对调一次相邻两堆石子的次序. 计算在上述条件下将n堆石子合并成一堆的最小得分. Input 输入数据共有二行,其中,第1行是石子堆数n≤100: 第2行是顺序排列的各堆石子数(≤20),每两个数之间用空格分隔. Output 输出合并的最小得分. Sample Input 3 2 5 1 Sample Out

石子合并 (动态规划)

一.试题 在一个园形操场的四周摆放N堆石子(N≤100),现要将石子有次序地合并成一堆.规定 每次只能选相邻的两堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分. 编一程序,由文件读入堆数N及每堆的石子数(≤20), ①选择一种合并石子的方案,使得做N-1次合并,得分的总和最小: ②选择一种合并石子的方案,使得做N-1次合并,得分的总和最大. 例如,所示的4堆石子,每堆石子数(从最上面的一堆数起,顺时针数)依 次为4594.则3次合并得分总和最小的方案:8+13+22=43 得分最大的

石子合并的动态规划问题

题目大概都是这样的: 设有N堆沙子排成一排,其编号为1,2,3,-,N(N<=300).每堆沙子有一定的数量,可以用一个整数来描述,现在要将这N堆沙子合并成为一堆,每次只能合并相邻的两堆,合并的代价为这两堆沙子的数量之和,合并后与这两堆沙子相邻的沙子将和新堆相邻,合并时由于选择的顺序不同,合并的总代价也不相同,如有4堆沙子分别为 1  3  5  2 我们可以先合并1.2堆,代价为4,得到4 5 2 又合并 1,2堆,代价为9,得到9 2 ,再合并得到11,总代价为4+9+11=24,如果第二步