hihoCoder 1636 Pangu and Stones
思路:区间dp.
状态:dp[i][j][k]表示i到j区间合并成k堆石子所需的最小花费。
初始状态:dp[i][j][j-i+1]=0
状态转移:
如果k等于1,dp[i][j][1]=min(dp[i][j][1],dp[i][k][s-1]+dp[k+1][j][1]+sum[j]-sum[i-1])(i<=k<j)
s从l到r,因为合并成一堆的时候是有堆数限制的
如果k大于1,dp[i][j][k]=min(dp[i][j][k],dp[i][s][k-1]+dp[s+1][j][1])(i<=s<j)
石子合并问题基本套路:先合并小区间,再合并大区间,所以第三维的s-1和1可以互换位置,因为无论怎样,小区间的值都已经确定
代码:
#include<bits/stdc++.h> using namespace std; #define ll long long #define pb push_back #define mem(a,b) memset(a,b,sizeof(a)) const int N=105; const int INF=0x3f3f3f3f; int dp[N][N][N]; int a[N],sum[N]; int main(){ ios::sync_with_stdio(false); cin.tie(0); int n,l,r; while(cin>>n>>l>>r){ for(int i=1;i<=n;i++)cin>>a[i],sum[i]=sum[i-1]+a[i]; mem(dp,INF); for(int i=1;i<=n;i++){ for(int j=i;j<=n;j++) dp[i][j][j-i+1]=0; } for(int d=1;d<=n;d++){ for(int i=1;i+d-1<=n;i++){ int j=i+d-1; for(int k=i;k<j;k++){ for(int s=l;s<=r;s++){ dp[i][j][1]=min(dp[i][j][1],dp[i][k][s-1]+dp[k+1][j][1]+sum[j]-sum[i-1]); } } for(int k=2;k<=n;k++){ for(int s=i;s<j;s++){ dp[i][j][k]=min(dp[i][j][k],dp[i][s][k-1]+dp[s+1][j][1]); } } } } if(dp[1][n][1]==INF)cout<<0<<endl; else cout<<dp[1][n][1]<<endl; } return 0; }