UVALive 4794 Sharing Chocolate

题目大意:给你一块c*l 的巧克力,要你正好分成n块,给你n块的面积,切巧克力时,只能横着或者竖着切,问你可不可能。

思路:我先开始自己的做法是按照面积,给每份一个id,然后每次切一份,dfs下去,看看可不可以,设d[ c ][ l  ][ s ],表示行数为c,列数为 l 的巧克力分成状态s可不可以,然后在记忆化一下,发现数组太大开不下,无奈之下,开为bool,一交是RE,虽然不懂为什么是RE,但那么大的数组,感觉自己肯定错了,这样来复杂度也有点高。

于是看了书上的,发现他做了个优化,其实数组里有很多状态是多余的,如果我们用sum[ s ] 来表示状态s的总面积,那么肯定是 c*l == s,而且d[ c ][ l  ][ s ]和d[ l ][ c  ][ s ] 这其实是同一个,那么我们就可以把状态精简为 d[ c ][ s ] 表示较小边为c,分成状态s可不可能,这样,空间复杂度和时间复杂度都减少了很多。然后有一点需要注意,就是在先开始就要判断输入的 c*l 是不是等于总面积和。

代码如下:

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

int a[22];
int sum[1<<16];

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

int vis[111][1<<16];
int d[111][1<<16];

int dfs(int c,int s)
{
    if(vis[c][s]) return d[c][s];
    vis[c][s] = 1;
    int& ans = d[c][s];
    if(bitcount(s)==1) return ans = 1;
    int l = sum[s]/c;
    for(int s0 = (s-1)&s ; s0 ;s0 = (s0-1)&s)
    {
        int s1 = s-s0;
        if(sum[s0]%c==0&&dfs(min(c,sum[s0]/c),s0)&&dfs(min(c,sum[s1]/c),s1)) return ans = 1;
        if(sum[s0]%l==0&&dfs(min(l,sum[s0]/l),s0)&&dfs(min(l,sum[s1]/l),s1)) return ans = 1;
    }
    return ans = 0;
}

int main()
{
    int cas = 0;
    int n;
    while(~scanf("%d",&n)&&n)
    {
        int c,l;
        scanf("%d%d",&c,&l);
        for(int i = 0;i<n;i++)
            scanf("%d",&a[i]);
        int S = (1<<n)-1;
        memset(sum,0,sizeof(sum));
        for(int i = 1;i<=S;i++)
            for(int j = 0;j<n;j++)
                if(i&(1<<j)) sum[i] += a[j];
        int ans = 0 ;
        if(sum[S]!=c*l) ans = 0;
        else
        {
            memset(vis,0,sizeof(vis));
            ans = dfs(min(c,l),S);
        }
        printf("Case %d: ",++cas);
        if(ans == 0)
            puts("No");
        else puts("Yes");
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值