动态规划:P1775石子合并(弱化版)区间DP、前缀和

P1775石子合并(弱化版)
题目:

 

 分析一下:

    这是一题最经典的区间DP,也是我学习区间DP的第一题。做题思路是构建DP数组dp[i][j],i、j分别代表区间的左右端点,思考状态转移方程,对于一个区间dp[i][j],(注意这里的区间并没有大小关系,只是从这一排石头上截取一部分的意思),把这个区间分割成两个,dp[i][i+k],dp[i+k+1][j],dp[i][j]一定是从这两堆石头合并起来的,也就是两个区间的代价合并起来的,但一定还要加上新的代价,也就是这两堆石子的总质量,我们可以用前缀和数组pre[j]-pre[i-1]来,但是我们k不确定,所以就要用一层循环来算出max值,所以得出状态转移方程dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+pre[j]-pre[i]);j>k>=i.,区间dp的思路就是先算出小区间的最优解在推出大区间的最优解,最后答案就是dp[1][n],整个区间的最优解,原因举例也就是算出长度为2的区间,长度为3会割成1 2 、2 1。必须要知道2,后面同理。所以最外层循环就要循环len,区间长度,从2->len.第二层循环区间的左端点l,知道左端点知道长度就可以算出右端点,在加上最内层循环,枚举分割点,注意分割点的取值是在左端点l 和右端点 r之间,并且注意r的取值<=n,推出l的取值 l+len-1<=n。取值范围是这题很需要注意的点,第二还要注意DP的边界条件和初始化问题:求最小代价,就把dp[i][j]初始化为INT_MAX,或者一个很大的值。这样才能算出最小值,注意dp[i][i]一定要初始化为0,因为单单一堆的时候代价为0,自己不用和自己合并。

    上代码:

 1 #include<iostream>
 2 #include<cstring>
 3 #include<string>
 4 #include<algorithm>
 5 #include<cmath>
 6 using namespace std;
 7 const int maxn =305;
 8 int dp[maxn][maxn];
 9 int a[maxn];
10 int pre[maxn];
11 int main()
12 {
13     int n;
14     cin >> n;
15     for (int i = 1; i <= n; ++i)
16     {
17         cin >> a[i];
18         pre[i] = pre[i - 1] + a[i];
19     }
20     for (int len = 2; len <= n; ++len)
21     {
22         for (int l = 1; l + len - 1 <= n; ++l)
23         {
24             int r = l + len - 1;
25             dp[l][r] = 0x7fffffff;//算最小值的时候给一个大值 便于计算最小值
26             for (int k = l; k < r; ++k)
27             {
28                 
29                 dp[l][r] = min(dp[l][r], dp[l][k] + dp[k + 1][r] + pre[r] - pre[l - 1]);
30             }
31         }
32 
33     }
34     cout << dp[1][n];
35     return 0;
36 
37 }

 

 

posted @ 2022-04-17 16:26  朱朱成  阅读(150)  评论(0编辑  收藏  举报