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 }