NKOJ3701 分享巧克力 [状态压缩]

NKOJ3701 分享巧克力 [状态压缩]

问题描述

给你一块长为x,宽为y的矩形巧克力。你可以对巧克力进行任意次下列操作:
每次操作可以沿一条直线把一块巧克力切割成两块巧克力,要求切出的两块巧克力的长和宽都是整数。
问:是否可以经过若干次上述操作,恰好得到n块面积分别为A1,A2,…,An的巧克力(巧克力要恰好用完,不能够有剩余)。如下图所示,给出你一块3*4的巧克力,我们可以将其切割成面积为1,2,3,6的四块巧克力。
img

输入格式

第一行,一个整数n,表示要求切割出的巧克力的块数。
第二行,两个整数x和y,表示初始巧克力的长和宽
第三行,n个空格间隔的整数,表示n块指定的巧克力的面积A1,A2,…,An

输出格式

一行,若能够切割成功输出”Yes”,否则输出”No”

样例输入 1

4
3 4
6 3 2 1

样例输出 1

Yes

解法

如果只标记巧克力的面积,会发现信息量不足。因为可能该面积存在一种合法分割方式,但是这种分割方式已经不能够用于当前巧克力(比如面积为6的巧克力需要被分成 23 2 ∗ 3 ,但是现在只有一块 16 1 ∗ 6 的巧克力)。

所以需要再加一维信息,用来确定巧克力的形状。

设当前状态为f[i][x],表示面积为i的巧克力的长为x. 比如对于样例,面积为 (1010)2 ( 1010 ) 2 表示选中了面积为6和2的巧克力。

一个显然的结论是:如果一个矩形能被分成另外两个同宽或同长的矩形,那么这个矩形一定存在。所以,当以下两种情况成立的时候原矩阵能够切割成功(设宽为y):

f[k][x]==true && f[i^k][x]==true(k belongs to i)

f[k][y]==true && f[i^k][y]==true(k belongs to i)

代码

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int nn,n,Mark[(1<<15)+5],f[105][(1<<15)+5],s[20];
int Lowbit(int x){return x&(-x);}
//也可以定成DFS(x,y,s)
bool DFS(int x,int s){
    if(f[x][s]!=-1)return f[x][s];
    f[x][s]=0;int y=Mark[s]/x;if(x>y)swap(x,y);
    if(s && s-Lowbit(s)==0){f[x][s]=1;return 1;}
    for(int s1=(s-1)&s;s1;s1=(s1-1)&s){
        int s2=s^s1;
        if(Mark[s1]%x==0)
            if(DFS(min(x,Mark[s1]/x),s1))
                if(DFS(min(x,Mark[s2]/x),s2))
                    {f[x][s]=1;return 1;}
        if(Mark[s1]%y==0)
            if(DFS(min(y,Mark[s1]/y),s1))
                if(DFS(min(y,Mark[s2]/y),s2))
                    {f[x][s]=1;return 1;}
    }
    return 0;
}
int main(){
    int n,x,y;scanf("%d",&n);
    scanf("%d%d",&x,&y);
    if(x>y)swap(x,y);
    nn=(1<<n)-1;
    memset(f,-1,sizeof(f));
    for(int i=0;i<n;i++)scanf("%d",&s[n-i]);
    for(int i=1;i<=nn;i++)
        for(int j=0;j<n;j++)
            if(i&(1<<j))Mark[i]+=s[j+1];
    if(Mark[nn]!=x*y){puts("No");return 0;}
    if(DFS(x,nn))puts("Yes");else puts("No");
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值