區間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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章