Sharing Chocolate UVALive - 4794(动态规划)

时间:2021-02-13 11:12:42

题目链接

https://vjudge.net/problem/UVALive-4794

题意

给出一块长为x,宽为y的矩形巧克力,每次操作可以沿一条直线把一块巧克力切割成两块长宽均为整数的巧克力。是否可以经过若干次操作后得到n块面积分别为a1、a2,…an的巧克力。

分析

先试试能不能画出状态转移图,每次操作有两种决策:横着切和竖着切。这两种决策都是始终合法的。横着切有x-1种状态,竖着切有y-1种状态。状态改变的量有长、宽和面积集合。然后我们试着给状态赋一个含义:设d(x,y,S)表示长为x,宽为y的巧克力是否可以切割成面积集合S。

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int maxn=16;
int n,d[1<<maxn][110],a[20],sum[1<<maxn];
int vis[1<<maxn][110];

int bitcount(int x)
{
    return x==0?0:(x&1)+bitcount(x>>1);
}

int dp(int S,int x)
{
    if(vis[S][x]) return d[S][x];
    vis[S][x]=1;
    int& ans=d[S][x];
    if(bitcount(S)==1) return ans=1;
    //枚举集合S的所有子集S0
    int y=sum[S]/x;
    for(int S0=(S-1)&S;S0;S0=(S0-1)&S)
    {
        int S1=S-S0;
        if(sum[S0]%x==0&&dp(S0,min(x,sum[S0]/x))&&dp(S1,min(x,sum[S1]/x)))
            return ans=1;
        if(sum[S0]%y==0&&dp(S0,min(y,sum[S0]/y)&&dp(S1,min(y,sum[S1]/y))))
            return ans=1;
    }
    return ans=0;
}

int main()
{
    int x,y,kase=0;
    while(~scanf("%d",&n) && n)
    {
        scanf("%d%d",&x,&y);
        for(int i=0;i<n;i++)
            scanf("%d",&a[i]);
        memset(sum,0,sizeof(sum));
        memset(vis,0,sizeof(d));
        for(int i=0;i<(1<<n);i++)
            for(int j=0;j<n;j++)
            if(i & (1<<j)) sum[i]+=a[j];
        int ans=0;
        if(sum[(1<<n)-1]==x*y && sum[(1<<n)-1]%x==0)
            ans=dp((1<<n)-1,min(x,y));
        printf("Case %d: %s\n",++kase,ans?"Yes":"No");
    }
    return 0;
}