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的巧克力需要被分成 2 3 ,但是现在只有一块 1 6 的巧克力)。

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

设当前状态为f[i][x],表示面积为i的巧克力的长为x. 比如对于样例,面积为 ( 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;
}

猜你喜欢

转载自blog.csdn.net/arliastark/article/details/80544109