12.石子合并 区间dp

Posted fx1998

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了12.石子合并 区间dp相关的知识,希望对你有一定的参考价值。

技术图片

 技术图片

 区间dp问题是在定义状态时,定义了一个区间

区间dp的状态表示一般是dp[i][j],表示从i到j这个区间,也就是从第i堆石子到第j堆石子这个区间

以最后一次合并的分界线的位置,来进行集合的划分

假设从i到j一共有k个,k = j - i + 1

按照左右两堆左边有几个来划分,左边有1个,2个,...,k - 1个

比如最后的两堆左边是[i, k],右边是[k + 1, j]

还是先减去最后一步,再求最值,再加上最后一步

dp[i][k] + dp[k + 1][j]                           +     s[j] - s[i - 1]

左边的最小代价 + 右边的最小代价    最后一步的最小代价

除了最后一步的最小代价        从第i堆到第j堆石子的总重量(前缀和)

技术图片

 时间复杂度:

状态数量:两维n * n  

状态计算:需要枚举k,k是O(n)的计算量

所以一共是n ^ 3

区间dp问题我们要保证在算每个dp[i][j]时,dp[i][j]用到的所有状态都已经计算好了

所以循环时有一个顺序

我们按照区间长度从小到大来做,区间长度从1开始,这样就可以保证我们在算某个状态时,这个状态所依赖的所有状态都是已经算好了的

技术图片

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 const int N = 310;
 4 int s[N]; //存储前缀和
 5 int dp[N][N];
 6 int main() {
 7     int n;
 8     cin >> n;
 9     for (int i = 1; i <= n; i++) {
10         cin >> s[i];
11         s[i] += s[i - 1];
12     }
13     //区间长度为1时,不需要合并,代价是0
14     for (int len = 2; len <= n; len++) { //按照长度从小到大枚举所有状态
15         for (int i = 1; len <= n - i + 1; i++) { //枚举起点
16             //cout << "len: " << len << " i: " << i << endl;
17             int l = i, r = i + len - 1; //区间的左右端点, len = r - l + 1的变形
18             //cout << "len: " << len << " l: " << l << " r: " << r << endl;
19             dp[l][r] = 1e9; //算最小值,需要初始化为一个较大的数
20             for (int k = l; k < r; k++) { //枚举分界点,从i到j - 1
21                 dp[l][r] = min(dp[l][r], dp[l][k] + dp[k + 1][r] + s[r] - s[l - 1]);   
22             }
23         }
24     }
25     cout << dp[1][n] << endl; //dp[1][n]就是答案
26     return 0;
27 }

 

以上是关于12.石子合并 区间dp的主要内容,如果未能解决你的问题,请参考以下文章

从合并石子学区间DP

石子合并(区间dp)

[NYIST737]石子合并(区间dp)

区间dp

石子合并 (区间DP)

区间dp