HDU 1693(状态压缩 插头DP)

 

我们引用国家队2008年陈丹琦的大作——《基于连通性状态压缩的动态规划问题》,上面对于插头、轮廓线的概念有详细的解释,不再赘述。

 

我们使用一个三维数组,前两维表示所在的格子,后一维表示轮廓线的状况,值为方案数。

 

在每一行开始前,我们需要把上一行最右的轮廓线转换为这一行最左的轮廓线,因此执行一次左移操作(实际上轮廓线是进行了向右的一次滑动)

 

HDU 1693(状态压缩 插头DP)

 

 

然后枚举所在单元格对每个轮廓线的影响 在论文中我们可以发现轮廓线总是包围一个格子的上部分和左部分 我们认为如果有通路与轮廓线重合则为1否则为0

 

 

HDU 1693(状态压缩 插头DP) 

 

因此很容易想象出来(当然也可以看论文)各个状态之间的转移关系 对于可通行的格子 分部分覆盖和全部覆盖以及全部不覆盖的情况讨论

 

对不可通行的格子必须将不可能的覆盖方案置为0 到最后的结果就是所求的方案了即dp[m][n][0]

 

感谢DK大牛的指导 本人5天来的苦思冥想终于得到了成果 1693 0ms 排行统计第一....

 

以下为本人代码:

 

 

#include  < iostream >
using   namespace  std;
typedef __int64 LL;
LL dp[
12 ][ 12 ][ 1 << 12 ];
long  hash[ 12 ][ 12 ];

int  main()
{
    
long  T;
    
long  b = 1 ;
    scanf(
" %ld " , & T);
    
while  (T -- )
    {
        
long  i,j,k,m,n;
        scanf(
" %ld %ld " , & m, & n);
        
for  (i = 1 ;i <= m; ++ i)
        {
            
for  (j = 1 ;j <= n; ++ j)
            {
                scanf(
" %ld " , & hash[i][j]);
            }
        }

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

        
for  (i = 1 ;i <= m; ++ i)
        {
            
long  len = 1 << n;
            
for  (j = 0 ;j < len; ++ j)
            {
                dp[i][
0 ][j << 1 ] = dp[i - 1 ][n][j];
            }

            
            
for  (j = 1 ;j <= n; ++ j)
            {
                len
= ( 1 << n << 1 );
                
for (k = 0 ;k < len; ++ k)
                {
                    
long  p = 1 << j;
                    
long  q = p >> 1 ;
                    
                    
bool  x = p & k;
                    
bool  y = q & k;

                    
if  (hash[i][j])
                    {
                        dp[i][j][k]
= dp[i][j - 1 ][k ^ p ^ q];
                        
if  (x != y)
                        {
                            dp[i][j][k]
+= dp[i][j - 1 ][k];
                        }
                    }
                    
else
                    {
                        
if  (x == 0 && y == 0 )
                        {
                            dp[i][j][k]
= dp[i][j - 1 ][k];
                        }
                        
else
                        {
                            dp[i][j][k]
= 0 ;
                        }
                    }
                }
            }
        }
        printf(
" Case %ld: There are %I64d ways to eat the trees.\n " ,b ++ ,dp[m][n][ 0 ]);

    }
    
return   0 ;
}

 

 

以下为DK大人的sample代码,我加上了注释:

 

#include < iostream >
using   namespace  std;
int  mat[ 100 ][ 100 ];
__int64 dp[
12 ][ 12 ][ 1 << 12 ];
int  main()
{
    
//  freopen("1.txt","r",stdin);
     int  zu;
    
int  g = 1 ;
    scanf(
" %d " , & zu);
    
while (zu -- )
    {
        
int  m,n;
        scanf(
" %d%d " , & m, & n);
        
int  i,j,k;
        
for (i = 1 ;i <= m;i ++ )
            
for (j = 1 ;j <= n;j ++ )
                scanf(
" %d " , & mat[i][j]);
            
// memset(dp,0,sizeof(dp));
            dp[ 0 ][n][ 0 ] = 1 ;
            
for (i = 1 ;i <= m;i ++ )
            {
                
for (j = 0 ;j < ( 1 << n);j ++ )
                    dp[i][
0 ][j << 1 ] = dp[i - 1 ][n][j];
                
// 把最右边的去掉 把所有的状态拉到下一行并进行合理的改变(注意最左边的是低位所代表的数) 
                
// 上一行最右边的边格一定不与轮廓线重合 下一行最左边的边格也一定不与轮廓线重合 
                
// 所以进行的是上一行所有状态(也就是轮廓线)向右的一次滑动
                     for (j = 1 ;j <= n;j ++ ) // 枚举决策线的拐向
                    {
                        
for (k = 0 ;k < ( 1 << n << 1 );k ++ ) // 枚举轮廓线
                        {
                            
int  p = 1 << j; // 第j个轮廓段(上)
                             int  q = p >> 1 ; // 第j-1个轮廓段(左)
                             bool  x = k & p; // 左轮廓是否与通路相交
                             bool  y = k & q; // 上轮廓是否与通路相交
                            
                            
// 判断轮廓线在[i,j]为拐向时的分布 每个格子有1<<n<<1种 多阶段决策
                            
                            
if (mat[i][j]) // 如果该单元可以通行
                            {
                                dp[i][j][k]
= dp[i][j - 1 ][k ^ p ^ q]; // 必然有一个通路连接上一个格的通路
                                 if (x != y)
                                    dp[i][j][k]
+= dp[i][j - 1 ][k]; // 有一处新覆盖则有另外的一种情况
                            }
                            
else // 否则为障碍格子
                            {
                                
if (x == 0 && y == 0 ) // 通路与轮廓线不相交
                                    dp[i][j][k] = dp[i][j - 1 ][k]; // 直接转移
                                 else
                                    dp[i][j][k]
= 0 ; // 通路与轮廓线相交的方案一定为0
                            }
                        }
                    }
            }
            printf(
" Case %d: There are %I64d ways to eat the trees.\n " ,g ++ ,dp[m][n][ 0 ]);
    }
    
return   0 ;

你可能感兴趣的:(HDU)