HDU 1693(状态压缩 插头DP)

 

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

 

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

 

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

 

 

 

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

 

 

 

 

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

 

对不可通行的格子必须将不可能的覆盖方案置为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;

posted @ 2008-09-04 11:20  Hdu-Lost  阅读(4059)  评论(2编辑  收藏  举报