HDU - 2280 Tetris Comes Back (状压DP)

Tetris Comes Back


Problem Description
Working in corporation is toilsome and rest is important. In leisure time, WisKey like to play Tetris. The Tetris game in board is N*5, and there are 8 kinds of blocks.
  Look that, red mean the area can’t be place. Other colors mean different kinds of blocks. If I give you C grey blocks (the 1st kind), and other infinite color blocks, Can you fulfil the board without any blocks overlap.
 

Input
Each case will contain two integers N (1<=N<=1000) and C (0<=C<=100). 
N*5 grid follow it. The ‘1’ represent red area, it can’t be place. The ‘0’ represent normal area.
Process cases to end of file.

 

Output
If you can full of the Tetris, print “YES”, otherwise, print “NO”.
 

Sample Input
  
  
1 1 00000 1 1 00100 1 1 01010 4 1 10000 00101 10010 00000
 

Sample Output
  
  
YES YES NO YES
 


题意:给你C个1*1的棋子,和无数个如图的2345678的棋子,问你能否拼成N*5大小的棋盘。其中棋盘有一些地方已经被填了。


解题思路:有点小激动,第一次靠自己做出了一道状压DP题。题目和SGU - 131 Hardwood floor (状压DP)是一类题目,但是这题多了许多条件。我们可以用dp[i][num][state]表示能否达到在第1~i-1行填满的情况下,用了num个1*1,第i行状态为state的状态。我们遍历每一行,每种num,每种状态,通过深搜来尝试放各种棋子,要判断好每种棋子能不能放。详看代码注释。




#include<iostream>
#include<deque>
#include<memory.h>
#include<stdio.h>
#include<map>
#include<string>
#include<algorithm>
#include<vector>
#include<math.h>
#include<stack>
#include<queue>
#include<set>
#define INF 1<<29
using namespace std;

int dp[1005][105][1<<6];
int sta[1005];//记录每一行的初始状态

int h,c;

// 当前行的初始状态,当前行的状态,下一行的状态,当前判断到第几列,当前是第几行,当前用了多少个1*1,初始状态是多少个1*1
void dfs(int prestate,int nowstate,int nextstate,int n,int cnt,int cnum,int fnum){

    //不能大于规定数量
    if(cnum>c)
        return;

    //当前行填完了
    if(n>=5){
        dp[cnt+1][cnum][nextstate]|=dp[cnt][fnum][prestate]&1;//状态转移
        return;
    }

    
    //如果当前行的第n列被填了,就跳过
    if(((1<<n)&nowstate)!=0){
        dfs(prestate,nowstate,nextstate,n+1,cnt,cnum,fnum);//下一个
        return;
    }


    //用1*1去填
    dfs(prestate,nowstate|(1<<n),nextstate,n+1,cnt,cnum+1,fnum);


    //用 | 去填
    if((nextstate&(1<<n))==0){
        dfs(prestate,nowstate|(1<<n),nextstate|(1<<n),n+1,cnt,cnum,fnum);
    }



    //用  |_  去填
    if(n!=0)
        if((nextstate&(1<<n))==0&&(nextstate&(1<<(n-1)))==0)
            dfs(prestate,nowstate|(1<<n),nextstate|(1<<n)|(1<<(n-1)),n+1,cnt,cnum,fnum);

    //用  _|  去填
    if(n<4)
        if((nextstate&(1<<n))==0&&(nextstate&(1<<(n+1)))==0)
            dfs(prestate,nowstate|(1<<n),nextstate|(1<<n)|(1<<(n+1)),n+1,cnt,cnum,fnum);

    
    if(n<4&&((1<<(n+1))&nowstate)==0){
        dfs(prestate,nowstate|(1<<n)|(1<<(n+1)),nextstate,n+2,cnt,cnum,fnum);//打横放 ——

        if((nextstate&(1<<n))==0)
            dfs(prestate,nowstate|(1<<n)|(1<<(n+1)),nextstate|(1<<n),n+2,cnt,cnum,fnum);//这样放 -|

        if((nextstate&(1<<(n+1)))==0)
            dfs(prestate,nowstate|(1<<n)|(1<<(n+1)),nextstate|(1<<(n+1)),n+2,cnt,cnum,fnum);//这样放  |-


        //放2*2
        if((nextstate&(1<<n))==0&&(nextstate&(1<<(n+1)))==0)
            dfs(prestate,nowstate|(1<<n)|(1<<(n+1)),nextstate|(1<<n)|(1<<(n+1)),n+2,cnt,cnum,fnum);

    }

}


int main(){

    while(~scanf("%d%d",&h,&c)){


        memset(dp,0,sizeof(dp));
        memset(sta,0,sizeof(sta));

        dp[1][0][0]=1;


        //初始化每行的状态
        char str[10];
        int sss=0;
        for(int i=1;i<=h;i++){
            scanf("%s",str);
            sss=0;
            for(int i=0;i<5;i++){
                if(str[i]=='1'){
                    sss|=(1<<(5-i-1));
                }
            }
            sta[i]=sss;

        }



        int state=1<<5;

        //遍历所有状态
        for(int i=1;i<=h;i++){

            for(int k=0;k<=c;k++)
                if(dp[i][k][0]==1)
                    dp[i][k][sta[i]]=1;

            for(int j=0;j<state;j++){
                j|=sta[i];
                
                for(int k=0;k<=c;k++)
                    dfs(j,j,sta[i+1],0,i,k,k);
            }
        }



        
        bool flag=0;
        for(int k=0;k<=c;k++)
            if(dp[h+1][k][0])
                flag=1;
        if(flag)
            printf("YES\n");
        else
            printf("NO\n");

    }

    return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值