hihocoder1636 Pangu and Stones(区间DP(石子合并变形))

题目链接:http://hihocoder.com/problemset/problem/1636

题目大意:有n堆石头,每次只能合并l~r堆,每次合并的花费是要合并的石子的重量,问你合并n堆石子的最小花费,若不能合并则输出0。

解题思路:

这算是石子合并的加强版了吧,原来石子合并是只能两堆两堆地合并,而现在对合并堆数加了一个范围[l,r]。这题我看到的时候也没什么想法,看了题解才会的,而且也看的不是特别懂。

首先定义数组dp[i][j][k]表示将[i,j]的石子合并成k堆需要的最小花费。

那么我们可以得到状态转移方程:

①p>1时,dp[i][j][p]=min(dp[i][j][p],dp[i][k][p-1]+dp[k+1][j][1]),(i=<k<j,2=<p<=r)

②p==1时,dp[i][j][1]=min(dp[i][j][1],dp[i][j][k]+sum[j]-sum[i-1]),(l=<k<=r)

之前还有几个疑问:

1、为什么①中,p范围是[2,r]而不是[l,r]?

答:[l,r]是的限制是给p==1时即合并的时候用的,单纯地划分区间并没有这个限制。

2、为什么①的状态转移方程是可行的,难道不应该写成dp[i][j][p]=min(dp[i][j][p],dp[i][k][p-x]+dp[k+1][j][x])吗?

答:如果手动模拟一下会发现,上面连个状态转移方程是等效的,所以①可以求出最优解,而且考虑到时间复杂度的问题,①省去了枚举x的花费,显然更优。

代码:

 1 #include<cstdio>
 2 #include<cmath>
 3 #include<cctype>
 4 #include<cstring>
 5 #include<iostream>
 6 #include<algorithm>
 7 #include<vector>
 8 #include<queue>
 9 #include<set>
10 #include<map>
11 #include<stack>
12 #include<string>
13 #define lc(a) (a<<1)
14 #define rc(a) (a<<1|1)
15 #define MID(a,b) ((a+b)>>1)
16 #define fin(name)  freopen(name,"r",stdin)
17 #define fout(name) freopen(name,"w",stdout)
18 #define clr(arr,val) memset(arr,val,sizeof(arr))
19 #define _for(i,start,end) for(int i=start;i<=end;i++)
20 #define FAST_IO ios::sync_with_stdio(false);cin.tie(0);
21 using namespace std;
22 typedef long long LL;
23 const int N=2e2+5;
24 const int INF=0x3f3f3f3f;
25 const double eps=1e-10;
26 
27 int sum[N];
28 int dp[N][N][N];
29 
30 int main(){
31     int n,l,r;
32     while(cin>>n>>l>>r){
33         memset(dp,0x3f,sizeof(dp));
34         for(int i=1;i<=n;i++){
35             cin>>sum[i];
36             sum[i]+=sum[i-1];
37         }
38         for(int i=1;i<=n;i++){
39             for(int j=i;j<=n;j++){
40                 dp[i][j][j-i+1]=0;
41             }
42         }
43         for(int len=1;len<n;len++){
44             for(int i=1;i+len<=n;i++){
45                 int j=i+len;
46                 //注意p要从2开始,而不是从l开始
47                 for(int p=2;p<=r;p++){
48                     for(int k=i;k<j;k++){
49                         if(k-i+1<p-1) continue;
50                         dp[i][j][p]=min(dp[i][j][p],dp[i][k][p-1]+dp[k+1][j][1]);
51                     }
52                 }
53                 for(int p=l;p<=r;p++){
54                     dp[i][j][1]=min(dp[i][j][1],dp[i][j][p]+sum[j]-sum[i-1]);
55                 }
56             }
57         }
58         if(dp[1][n][1]==INF)
59             cout<<0<<endl;
60         else
61             cout<<dp[1][n][1]<<endl;
62     }
63     return 0;
64 }
原文地址:https://www.cnblogs.com/fu3638/p/8895522.html