POJ 2411 Mondriaan's Dream 状态压缩 搜索

题意:

求用1 * 2 的矩行拼h * w的矩阵方法数(h,w <=11)

算法:

1.朴素算法,根据关系,枚举

由于在做第i行dp时必须完全覆盖第i-1行,只要抓住这个条件不放就行。

1、如果第i行中有0,则第i-1行一定为1;

2、如果第i行中为1的x列第i-1行为0,说明第i行肯定是竖着放的;

3、如果第i行中为1的x列第i-1行的该列也为1,可能性只有一个,第i行是横放的,所以第i行的x+1列也必须为1,又因为第i行的x+1列为1是因为横着放的,所以第i-1行的x+1列也必须为1。

View Code
//自己没写枚举暴力的代码了,速度很慢2000ms,代码来自
//http://gisyhy.blog.163.com/blog/static/129390343200992441558735/ 
#include <iostream>
using namespace std;

#define  N  11+1
#define  S   (1<<12)
#define  LL  long long
LL dp[N][S];
bool a[S];
int n,Maxn,h,w;

bool Ok(int i)//初始化第1行状态为i时是否容许
{
 for(int k = 0 ; k < w; )
 {
  if(i & (1<<k))
  {
   if(k == w-1 || !(i & (1<<(k+1))))
    return false;

   k+= 2;
  }
  else k ++;
 }
 return true;
}

bool Pk(int i,int j)//判断i和j状态有无冲突
{
 int k;
 for(k = 0; k < w; )
 {
  if((i & (1<<k)) == 0)
  {
   if((j & (1<<k)) == 0)//如果第i行为0,则第j行一定得为1
    return false;
   k ++;
  }
  else
  {
   if(j & (1<<k))
    if(k == w-1 || ((i & (1<<(k+1))) && (j & (1<<(k+1)))) == 0)//我就是在这里倒的霉
     //k == w-1 || ((i & (1<<(k+1))) || (j & (1<<(k+1))))这样就少考虑了一种情况,哎呀,不爽!!
     return false;
    else k +=2;
   else k++;
  }
 }
 return true;
}
int main()
{

 while(scanf("%d %d",&h,&w),h && w)
 {
  int i,j,k;

  if(h < w)//将状态数取小,优化处理
   i = h ,h = w,w =i;

  Maxn = (1<<w)-1;
  memset(dp,0,sizeof(dp));

  for(i = 0 ; i <= Maxn; i++)//初始化第1行
  {
   if(Ok(i))
    dp[h][i] =1;
  }

  for(k = h-1 ; k >=1; k --)//枚举第k行
  {
   for(i = 0 ; i <= Maxn; i++)//当前行的状态
   {
    for(j = 0; j <= Maxn ;j++)//前一行的状态
    {
     if(Pk(i,j))
      dp[k][i] +=dp[k+1][j];
    }
   }
  }

  cout<<dp[1][Maxn]<<endl;
 }
 return 0;
}

 

2.DFS搜索(奇妙)

View Code
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<algorithm>
using namespace std;
__int64 dp[2][4100], cow;
/*
状态1表示放了,0表示没有放

 
1. 竖直放置 (上行该列状态为0,当前行该列状态为1) 
2. 横着放置  上行该两列状态为1, 此行该两列状态为1 
3. 不放  //上行该列状态为1,当前行该列状态为0 


*/ 
 
void DFS( int c, int s1, int s2, int m) //s1当前行状态, s2上一行状态 
{
  if( c > m )
      return;
  if( c == m )
  {
    dp[cow%2][s1] += dp[(cow-1)%2][s2];        
    return;    
  }
  DFS(c + 1, (s1<<1)|1, s2<<1, m ); //竖直放置 
  DFS(c + 1, s1<<1, s2<<1|1, m); //不放 
  DFS(c + 2, (s1<<2)|3, (s2<<2)|3, m);  //横着放置  
}

void DFS_First(int c, int s1, int m)
{
  if( c > m)
      return; 
  if( c == m )
  {
    dp[1][s1]++;        
    return;    
  }
  DFS_First(c + 1, s1<<1,  m); //不放 
  DFS_First(c + 2, (s1<<2)|3, m);  //横着放置  
     
}

int main( )
{
  int N, M;
  while( scanf("%d%d",&N,&M), N && M)
  {
     if( (N * M) % 2 )
     {
       puts("0");
       continue;    
     }
     if( N > M )
         swap(N, M);
     memset(dp, 0, sizeof(dp));
     DFS_First(0, 0, M);
     for( cow = 2; cow <= N ; cow++)
     {
       DFS(0, 0, 0, M);
       memset(dp[(cow-1)%2], 0, sizeof(dp[0]));
     }
     printf("%I64d\n", dp[N % 2][(1<<M) - 1]);
  }   
  return 0;  
}

转载于:https://www.cnblogs.com/tangcong/archive/2012/08/06/2625502.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值