UVA1099----Sharing Chocolate----在集合上的DP

题目地址:http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=3540

题目意思:

给你一块X*Y的巧克力

问你是否可以分成N块大小分别为AI的小巧克力

解题思路:

我们用F[X][S]表示能否分成将一个小边为X且集合为S

切的时候分两种,横切和竖切

横切则是X不变,竖切则是Y不变,可以切成两个子集,按记忆化搜索

对于那些x*y不等于sum[s]的我们可以直接不计算,因为无法满足,没有计算的必要

下面上代码:

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

const int maxn = 16;
const int maxs = 1<<maxn;
const int maxx = 110;

int a[maxn];
int f[maxx][maxs];
bool vis[maxx][maxs];
int sum[maxs];
int x,y,n;
int all;

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

int dp(int S,int x)
{
    if(vis[x][S])
        return f[x][S];
    vis[x][S]=1;
    int &ans = f[x][S];
    if(bitcount(S)==1)
        return ans=1;
    int y = sum[S]/x;

    //枚举子集S0
    for(int S0=(S-1)&S;S0;S0=(S0-1)&S)
    {
        int S2 = S-S0;
        if(sum[S0]%x==0 && dp(S0,min(x,sum[S0]/x)) && dp(S2,min(x,sum[S2]/x)))
            return ans = 1;
        if(sum[S0]%y==0 && dp(S0,min(y,sum[S0]/y)) && dp(S2,min(y,sum[S2]/y)))
            return ans = 1;
    }

    return ans = 0;
}

int main()
{
    int ca = 1;
    while(~scanf("%d",&n) && n)
    {
        memset(sum,0,sizeof(sum));
        scanf("%d%d",&x,&y);
        for(int i=0;i<n;i++)
            scanf("%d",&a[i]);
        for(int s=0;s<(1<<n);s++)
        {
            for(int i=0;i<n;i++)
            {
                if(s&(1<<i))
                    sum[s]+=a[i];
            }
        }
        all = (1<<n)-1;
        memset(vis,false,sizeof(vis));
        int ans;
        if(sum[all] != x*y || sum[all]%x!=0)
            ans = 0;
        else
            ans = dp(all,min(x,y));

        printf("Case %d: %s
",ca++,ans?"Yes":"No");

    }
    return 0;
}


原文地址:https://www.cnblogs.com/pangblog/p/3244060.html