pangu and stones(區間dp)

題意:有N堆石子,每次能夠合併連續的、大於等於L、小於等於R堆石子,代價是這些石子的個數和。問合併成一堆石子的代價最小值。
使用一個dp[l][r][k]記錄將區間[l,r]的石子合併爲k堆需要的最小代價。用一個d[i]記錄第k堆石子有幾個石子
轉移方程:dp(l,r,k)=min{dp(l,i,1)+dp(i+1,r,k-1)},l<=i<k
                dp(l,r,1)=min{dp(l,r,x)}+(d[l]+…+d[r]),L<=x<=R


          //當時在現場賽的時候沒推出來轉移方程,胡亂分析了四個小時,甚至隊員想出了dfs離散化之類的騷操作...然並卵還是沒做出來然後打鐵回家
#include <bits/stdc++.h>


using namespace std;
const int maxn=120,inf=1000000;

int dp[maxn][maxn][maxn],d[maxn],mi,ma;
int n;
int init()
{

    for(int i=1;i<=n;i++)
    {
        cin>>d[i];
    }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            for(int k=1;k<=n;k++)
            {
                dp[i][j][k]=-1;
            }
     for(int i=1;i<=n;i++)
    {
        dp[i][i][1]=d[i];
    }
}
int solve(int l,int r,int k)
{
    if(dp[l][r][k]==-2)return -2;
    if(l==r&&dp[l][r][k]!=-1)return 0;
    if(dp[l][r][k]!=-1)return dp[l][r][k];
    if(k==1)
    {int m=inf;
        if(l!=r+1)
        {

        for(int i=mi;i<=ma;i++)
        {
            int cur=solve(l,r,i);
            if(m>cur&&cur>=0){m=cur;}
        }

        }
        else m=0;
         if(m!=inf)

            for(int i=l;i<=r;i++)
        {
            m+=d[i];
        }

        else m=-2;
        dp[l][r][1]=m;
        return m;
    }
    int m=inf;
    for(int i=l;i<r;i++)
    {
        int cur1=solve(l,i,1),cur2=solve(i+1,r,k-1);
        if(cur1==-2||cur2==-2)continue;//{dp[l][r][k]=-2;return -2;}
        if(m>cur1+cur2){ if(l==i)cur1=0;if(i+1==r)cur2=0;  m=cur1+cur2;}
    }
    if(m>=inf){dp[l][r][k]=-2;return -2;}
    else dp[l][r][k]=m;
    return m;
}

int main()
{

    while(    cin>>n>>mi>>ma)
    {init();
    int cc=solve(1,n,1);
    if(cc<0)cc=0;
    cout<<cc<<endl;
    }

    return 0;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章