区间dp(Pangu and Stones)

题意:给n堆石头,标号为1到n,每堆石头有一定的个数,每次可以选择[L,R]中任意一个数那么多堆来合并,每次合并的花费是合并后石头的的总个数,求花费的最小值
思路:dp[i][j][p]代表把区间[i,j]的石头分成p堆的最小花费,所以对于i到j这段区间,我们枚举中间的断点k,可以把区间分成 i 到 k 和 k+1 到 j 两个区间,把 i 到 j 分成 p 堆的最小花费,就相当于把 i 到 k 分成x堆和 k+1 到 j 分成p-x堆的最小花费相加。

// 即
dp[i][j][p]=min(dp[i][j][p],dp[i][k][x]+dp[k+1][j][p-x]);
//但是这样操作就有N^5复杂度,N=100也卡死,但是莫名奇妙的是只要管x=1的时候就ok,本人也搞不懂.望大神指出。于是简化
dp[i][j][p]=min(dp[i][j][p],dp[i][k][1]+dp[k+1][j][p-1]);

然后当p>=L的时候,我们就可以考虑将这些合并,选择将其合并和继续分为其他区间形成的花费中的较小值.

#include<iostream>
#include<algorithm>
#include<vector>
#include<set>
#include<time.h>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<functional>
#include<stack>
#include<map>
#include<queue>
#define mod (10007)
#define middle (l+r)>>1
#define SIZE 1000000+5
#define lowbit(x) (x&(-x))
#define lson (rt<<1)
#define rson (rt<<1|1)
typedef long long ll;
typedef long double lb;
const int inf_max = 0x3f3f3f;
const ll Linf = 9e18;
const int maxn = 100+10;
const long double E = 2.7182818;
const double eps=0.0001;
using namespace std;
inline int read()
{
    int f=1,res=0;
    char ch=getchar();
    while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
    while(ch>='0'&&ch<='9') { res=res*10+ch-'0' ; ch=getchar(); }
    return f*res;
}
int dp[maxn][maxn][maxn],n,L,R,sum[maxn];

int main()
{
    while(~scanf("%d%d%d",&n,&L,&R))
    {
        memset(sum,0,sizeof(sum));
        memset(dp,inf_max,sizeof(dp));
        for(int i=1;i<=n;i++) {
            int stone;
            scanf("%d",&stone);
            sum[i]=sum[i-1]+stone;
            dp[i][i][1]=0;
        }
        for(int len=2;len<=n;len++)
            for(int l=1;l<=n;l++)
            {
                int r=l+len-1;
                if(r>n) break;
                for(int p=2;p<=min(R,len);p++)   //因为后面会把p堆合在一起,上限取R
                {
                    for(int k=l;k<r&&p<=r+1-k;k++)
                        dp[l][r][p]=min(dp[l][r][p],dp[l][k][1]+dp[k+1][r][p-1]);
                    if(p>=L) dp[l][r][1]=min(dp[l][r][1],dp[l][r][p]+sum[r]-sum[l-1]);  //考虑将其合并,当然也可以继续化为两个区间花费之和,取较小值。
                }
            }
        if(dp[1][n][1]>=inf_max) cout<<0<<endl;
        else cout<<dp[1][n][1]<<endl;
    }
    return 0;
}
发布了33 篇原创文章 · 获赞 14 · 访问量 416

猜你喜欢

转载自blog.csdn.net/qq_44077455/article/details/103880755