hihoCoder 1636 Pangu and Stones

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;
}
原文地址:https://www.cnblogs.com/widsom/p/8321697.html