Pangu and Stones(石子合并 记忆化搜索)

原题: https://cn.vjudge.net/problem/HihoCoder-1636

题意:

给出n堆石子,每次可以合并 [ L , R ] [L,R] [L,R]堆,花费 s u m sum sum,求合并到一堆的最小花费。

解析:

显然和合并两堆差不多,我们可以记忆化搜出 d p [ i ] [ j ] [ k ] dp[i][j][k] dp[i][j][k]表示 [ i , j ] [i,j] [i,j]中已经合并到 k k k堆的花费。

这类题也很套路,看上去分成 k k k堆要枚举,为阶乘复杂度,但是我们只需要前面取一堆,转移到 k − 1 k-1 k1的状态即可。

代码:

#include
using namespace std;
#define ll long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
const ll inf=1e17;
const int N=105;
int down,up,n;
ll dp[N][N][N],a[N];

ll dfs(int l,int r,int k)
{
    if(k<1||l>r)return inf;
    int len=r-l+1;
    ll sum=a[r]-a[l-1];
    ll &D=dp[l][r][k];
    if(D<inf)return D;

    if(len<k)return inf;
    if(len==k){
        return 0;
    }
    if(k==1){
        if(len<down)return inf;
        if(len<=up&&len>=down)return D=min(D,sum);
        rep(i,down,up){
            D=min(D,dfs(l,r,i)+sum);
        }
        return D;
    }

    D=min(D,dfs(l+1,r,k-1));
    rep(i,l+down-1,r-1){
        if(r-i==k-1||r-i>=k-2+down){
            D=min(D,dfs(l,i,1)+dfs(i+1,r,k-1));
        }
    }
    return D;
}
int main()
{
    while(~scanf("%d%d%d",&n,&down,&up))
    {
        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]=inf;
        for(int i=1;i<=n;i++)
            scanf("%lld",&a[i]),a[i]+=a[i-1],dp[i][i][1]=0;
        ll ans=dfs(1,n,1);
        printf("%lld\n",ans>=inf?0:ans);
    }
    return 0;
}

你可能感兴趣的:(图论/搜索)