POJ 2411 Mondriaan's Dream 解题报告

POJ 2411 Mondriaan's Dream 解题报告2

与1不同的是,没用dfs,纯dp
思路是:
用0表示没放,1表示放了。横放则左右两格都是1,竖放则上格是0,下格是1。
这种记录state的方法决定了:如果上下两行的state是确定的,那么放法唯一。
Fun(state,n)表示n这行的状态是state的时候有多少种放法。
那么我们要求的就是Fun(2^m-1,n).
Fun(state,n)就等于sigma Fun(last,n-1),last取遍所有可以取到的状态。
限制last取值的因素有两个:
1、state中是0的位置,last中一定是1,否则出现没填满的情况。
2、把state和last取&,也就是last是0的位,state也变0,看有没有影响到state,让它出现单1不能横放。
结束条件是n==1,且state能横放。

还有一个要注意的问题就是结果要用64位整型保存。
  1
  2#include <cstdio> 
  3
  4const int large = 1<<11;
  5int m,n;
  6__int64 result[12][12];
  7__int64 dp[large][12];
  8int power;
  9
 10__int64 Fun( const int state, const int n )
 11{
 12    if ( n == 1 )
 13    {
 14         int flag = 1;
 15         for ( int i = 0; i < power; ++i )
 16         {
 17             if ( (state&(1<<i)) == 0 )continue;
 18             if ( i+1 == m )
 19             {
 20                  flag = 0;
 21                  break;
 22             }

 23             if ( (state&(1<<(i+1))) == 0 )
 24             {
 25                  flag = 0;
 26                  break;
 27             }

 28             ++i;
 29         }

 30         if ( flag ) return 1//若能横放
 31         return 0;
 32    }

 33    
 34     int last = ~state;//last是last状态1起码的状态 
 35     last &= ( (1<<m) -1 );//我一开始忘记了加这一句,last全都是负的,哈哈
 36     __int64 s = 0;
 37     if ( dp[state][n] != -1 ) return dp[state][n];
 38     
 39     for ( int i = 0; i < power; ++i )
 40     {
 41         int flag = 1;
 42         int tmp = state&i;
 43         if ( (i&last) != last ) continue//last限制因素1
 44         for ( int j = 0; j < m; ++j )//last限制因素2
 45         {
 46             if ( (tmp&(1<<j)) == 0 ) continue;
 47             if ( j+1 == m )
 48             {
 49                  flag = 0;
 50                  break;
 51             }

 52             if ( (tmp&(1<<(j+1))) == 0 )
 53             {
 54                  flag = 0;
 55                  break;
 56             }

 57             ++j;
 58         }

 59         if ( flag ) s+= Fun( i, n-1 );
 60     }

 61     
 62     dp[state][n] = s;
 63     return s;
 64}

 65
 66int main()
 67{
 68    while ( scanf( "%d%d"&m, &n ), !( m == 0 && n == 0 ) )
 69    {
 70          int s = 0;
 71          if ( (m*n)%2 ) //面积
 72          {
 73               puts( "0" );
 74               continue;
 75          }

 76          if ( m > n ) //使n大m小
 77          {
 78               int tmp = m;
 79               m = n;
 80               n = tmp;
 81          }

 82          if ( result[n][m] != 0 ) //算过就不要再算了
 83          {
 84               printf( "%I64d\n", result[n][m] );
 85               continue;
 86          }

 87          power = 1<<m;
 88          //初始化
 89          for ( int i = 0; i < large; ++i )
 90          {
 91              for ( int j = 0; j <= n; ++j )
 92              {
 93                  dp[i][j] = -1;
 94              }

 95          }

 96          
 97          result[n][m] = Fun( power-1, n ); //最后一行放满
 98          printf( "%I64d\n", result[n][m] );
 99    }

100    
101    return 0;
102}

103
posted @ 2008-07-21 11:08  王婷婷๑→‿ฺ←๑  阅读(2799)  评论(1编辑  收藏  举报