插头DP小结(ACM by kuangbin)

这几天学习了一下插头DP,刷了11道题。对插头DP有了点理解。插头DP就先告一段落吧! 后面还有插头DP的广义路径和其它复杂应用,以后有机会再补上吧!

      kuangbin

 

 

首先入门推荐的还是cdq的论文:《基于连通性状态压缩的动态规划问题》

http://wenku.baidu.com/view/4fe4ac659b6648d7c1c74633.html

上面的论文关于插头和轮廓线等概念讲得很清楚。而且关于插头DP的精髓也讲得很透了。

插头DP其实就是每格进行状态转移。

看懂原理和写代码还是有段距离的,可以结合代码去加深理解原理。同时形成适合自己风格的代码模板。

 

HDU 1693  Eat the Trees  此题是比较基础的入门题。

多条回路,求回路数问题。格子有障碍格子和非障碍格子两种。

解题报告

http://www.cnblogs.com/kuangbin/archive/2012/10/01/2709834.html

HDU 1693
#include<stdio.h>

#include<string.h>

#include<iostream>

#include<algorithm>

using namespace std;



const int HASH=10007;

const int STATE=1000010;//状态数

const int MAXD=15;

int N,M;

int code[MAXD],maze[MAXD][MAXD];



struct HASHMAP

{

    int head[HASH],next[STATE],state[STATE],size;

    long long f[STATE];

    void init()

    {

        size=0;

        memset(head,-1,sizeof(head));

    }

    void push(int st,long long ans)

    {

        int i,h=st%HASH;

        for(i=head[h];i!=-1;i=next[i])

          if(st==state[i])

          {

              f[i]+=ans;

              return;

          }

        f[size]=ans;

        state[size]=st;

        next[size]=head[h];

        head[h]=size++;

    }

}hm[2];



void decode(int *code,int m,int st)

{

    int i;

    for(i=m;i>=0;i--)

    {

        code[i]=st&1;

        st>>=1;

    }

}

int encode(int *code,int m)

{

    int i,st=0;

    for(int i=0;i<=m;i++)

    {

        st<<=1;

        st|=code[i];

    }

    return st;

}

void init()

{

    int i,j;

    scanf("%d%d",&N,&M);

    for(i=1;i<=N;i++)

      for(int j=1;j<=M;j++)

        scanf("%d",&maze[i][j]);

    for(int i=1;i<=N;i++)maze[i][M+1]=0;//边界补0

    for(int i=1;i<=M;i++)maze[N+1][i]=0;

}



void shift(int *code,int m)//换行的时候移位

{

    int i;

    for(i=m;i>0;i--)

      code[i]=code[i-1];

    code[0]=0;

}



void dpblank(int i,int j,int cur)

{

    int k,left,up;

    for(k=0;k<hm[cur].size;k++)

    {

        decode(code,M,hm[cur].state[k]);

        left=code[j-1];

        up=code[j];

        if(left&&up)//11  -> 00

        {

            code[j-1]=code[j]=0;

            if(j==M)shift(code,M);

            hm[cur^1].push(encode(code,M),hm[cur].f[k]);

        }

        else if(left||up)//01 或 10

        {

            if(maze[i][j+1])

            {

                code[j-1]=0;

                code[j]=1;

                hm[cur^1].push(encode(code,M),hm[cur].f[k]);

            }

            if(maze[i+1][j])

            {

                code[j-1]=1;

                code[j]=0;

                if(j==M)shift(code,M);//这个不要忘了!

                hm[cur^1].push(encode(code,M),hm[cur].f[k]);

            }

        }

        else

        {

            if(maze[i][j+1]&&maze[i+1][j])

            {

                code[j]=code[j-1]=1;

                hm[cur^1].push(encode(code,M),hm[cur].f[k]);

            }

        }

    }

}



void dpblock(int i,int j,int cur)

{

    int k;

    for(k=0;k<hm[cur].size;k++)

    {

        decode(code,M,hm[cur].state[k]);

        code[j-1]=code[j]=0;

        if(j==M)shift(code,M);

        hm[cur^1].push(encode(code,M),hm[cur].f[k]);

    }

}



void solve()

{

    int i,j,cur=0;

    long long ans=0;

    hm[cur].init();

    hm[cur].push(0,1);

    for(i=1;i<=N;i++)

      for(j=1;j<=M;j++)

      {

          hm[cur^1].init();

          if(maze[i][j])dpblank(i,j,cur);

          else dpblock(i,j,cur);

          cur^=1;

      }

    for(i=0;i<hm[cur].size;i++)

      ans+=hm[cur].f[i];

    printf("There are %I64d ways to eat the trees.\n",ans);

}

int main()

{

    //freopen("in.txt","r",stdin);

    //freopen("out.txt","w",stdout);

    int T;

    int iCase=0;

    scanf("%d",&T);

    while(T--)

    {

        iCase++;

        printf("Case %d: ",iCase);

        init();

        solve();

    }

    return 0;

}

 

Ural  1519  Formula 1  和论文上的同一道题。此题是求单回路数问题,和上面的一题在状态转移时有点不同。就是形成回路一定要在最后一个非障碍格子。

我的代码是用最小表示法做的。

解题报告

http://www.cnblogs.com/kuangbin/archive/2012/09/29/2708989.html

ural 1519
/*

最小表示法

*/



#include<stdio.h>

#include<iostream>

#include<string.h>

#include<algorithm>

using namespace std;



const int MAXD=15;

const int HASH=30007;

const int STATE=1000010;



int N,M;

int maze[MAXD][MAXD];

int code[MAXD];

int ch[MAXD];//最小表示法使用

int ex,ey;//最后一个非障碍格子的坐标

struct HASHMAP

{

    int head[HASH],next[STATE],size;

    long long state[STATE];

    long long f[STATE];

    void init()

    {

        size=0;

        memset(head,-1,sizeof(head));

    }

    void push(long long st,long long ans)

    {

        int i;

        int h=st%HASH;

        for(i=head[h];i!=-1;i=next[i])//这里要注意是next

          if(state[i]==st)

          {

              f[i]+=ans;

              return;

          }

        state[size]=st;

        f[size]=ans;

        next[size]=head[h];

        head[h]=size++;

    }

}hm[2];

void decode(int *code,int m,long long  st)

{

    for(int i=m;i>=0;i--)

    {

        code[i]=st&7;

        st>>=3;

    }

}

long long encode(int *code,int m)//最小表示法

{

    int cnt=1;

    memset(ch,-1,sizeof(ch));

    ch[0]=0;

    long long st=0;

    for(int i=0;i<=m;i++)

    {

        if(ch[code[i]]==-1)ch[code[i]]=cnt++;

        code[i]=ch[code[i]];

        st<<=3;

        st|=code[i];

    }

    return st;

}

void shift(int *code,int m)

{

    for(int i=m;i>0;i--)code[i]=code[i-1];

    code[0]=0;

}

void dpblank(int i,int j,int cur)

{

    int k,left,up;

    for(k=0;k<hm[cur].size;k++)

    {

        decode(code,M,hm[cur].state[k]);

        left=code[j-1];

        up=code[j];

        if(left&&up)

        {

            if(left==up)//只能出现在最后一个非障碍格子

            {

                if(i==ex&&j==ey)

                {

                    code[j-1]=code[j]=0;

                    if(j==M)shift(code,M);

                    hm[cur^1].push(encode(code,M),hm[cur].f[k]);

                }

            }

            else//不在同一个连通分量则合并

            {

                code[j-1]=code[j]=0;

                for(int t=0;t<=M;t++)

                  if(code[t]==up)

                    code[t]=left;

                if(j==M)shift(code,M);

                hm[cur^1].push(encode(code,M),hm[cur].f[k]);

            }

        }

        else if((left&&(!up))||((!left)&&up))

        {

            int t;

            if(left)t=left;

            else t=up;

            if(maze[i][j+1])

            {

                code[j-1]=0;

                code[j]=t;

                hm[cur^1].push(encode(code,M),hm[cur].f[k]);

            }

            if(maze[i+1][j])

            {

                code[j-1]=t;

                code[j]=0;

                if(j==M)shift(code,M);

                hm[cur^1].push(encode(code,M),hm[cur].f[k]);

            }

        }

        else//无插头,则构造新的连通块

        {

            if(maze[i][j+1]&&maze[i+1][j])

            {

                code[j-1]=code[j]=13;

                hm[cur^1].push(encode(code,M),hm[cur].f[k]);

            }

        }

    }

}

void dpblock(int i,int j,int cur)

{

    int k;

    for(k=0;k<hm[cur].size;k++)

    {

        decode(code,M,hm[cur].state[k]);

        code[j-1]=code[j]=0;

        if(j==M)shift(code,M);

        hm[cur^1].push(encode(code,M),hm[cur].f[k]);

    }

}

char str[MAXD];

void init()

{

    memset(maze,0,sizeof(maze));



    ex=0;

    for(int i=1;i<=N;i++)

    {

        scanf("%s",&str);

        for(int j=0;j<M;j++)

        {

            if(str[j]=='.')

            {

                ex=i;

                ey=j+1;

                maze[i][j+1]=1;

            }

        }

    }

}

void solve()

{

    int i,j,cur=0;

    long long ans=0;

    hm[cur].init();

    hm[cur].push(0,1);

    for(i=1;i<=N;i++)

      for(j=1;j<=M;j++)

      {

          hm[cur^1].init();

          if(maze[i][j])dpblank(i,j,cur);

          else  dpblock(i,j,cur);

          cur^=1;

      }

    for(i=0;i<hm[cur].size;i++)

      ans+=hm[cur].f[i];

    printf("%I64d\n",ans);

}

int main()

{

    //freopen("in.txt","r",stdin);

    //freopen("out.txt","w",stdout);

    while(scanf("%d%d",&N,&M)!=EOF)

    {

        init();

        if(ex==0)//没有空的格子

        {

            printf("0\n");

            continue;

        }

        solve();

    }

    return 0;

}

 

 

 

FZU 1977  Pandora adventure  此题也是单回路数问题。但是格子有了三种:障碍格子,必走格子和选择性经过格子。这样导致最后一个非障碍格子不确定。所以增加一个标志位来记录是否形成回路。

解题报告

http://www.cnblogs.com/kuangbin/archive/2012/10/01/2709967.html

FZU 1977
/*

FZU 1977

简单回路数问题,N*M(1<=N,M<=12)的方格中有障碍点,必走点和非必走点

因为回路只有一条,而形成回路的最后一个点又是不确定的。

所以额外增加一个标志位来记录是否形成回路。

如果已经形成回路,而后面又遇到插头或者必须走的点,则该方案不合法

G++ 375ms

*/

#include<stdio.h>

#include<iostream>

#include<algorithm>

#include<string.h>

using namespace std;



const int MAXD=15;

const int HASH=10007;

const int STATE=100010;



int N,M;

int code[MAXD];

int maze[MAXD][MAXD];//0表示障碍点,1表示非必走点,2表示必走点

int ch[MAXD];

int isend;//是否形成回路标记位



struct HASHMAP

{

    int head[HASH],next[STATE],size;

    long long state[STATE],f[STATE];

    void init()

    {

        size=0;

        memset(head,-1,sizeof(head));

    }

    void push(long long st,long long ans)

    {

        int i,h=st%HASH;

        for(i=head[h];i!=-1;i=next[i])

            if(state[i]==st)

            {

                f[i]+=ans;

                return;

            }

        state[size]=st;

        f[size]=ans;

        next[size]=head[h];

        head[h]=size++;

    }

}hm[2];



void decode(int *code,int m,long long st)//注意要增加一个isend标记位

{

    for(int i=m;i>=0;i--)

    {

        code[i]=st&7;

        st>>=3;

    }

    isend=st&1;//isend标记位在st的最高一位

}

long long encode(int *code,int m)//增加isend标记位

{

    long long st=isend;//最高位



    int cnt=1;

    memset(ch,-1,sizeof(ch));

    ch[0]=0;

    for(int i=0;i<=m;i++)

    {

        if(ch[code[i]]==-1)ch[code[i]]=cnt++;

        code[i]=ch[code[i]];

        st<<=3;

        st|=code[i];

    }

    return st;

}

void shift(int *code,int m)

{

    for(int i=m;i>0;i--)code[i]=code[i-1];

    code[0]=0;

}



void dpblank(int i,int j,int cur)

{

    int k,left,up;

    for(k=0;k<hm[cur].size;k++)

    {

        decode(code,M,hm[cur].state[k]);

        left=code[j-1];

        up=code[j];

        if(isend)

        {//如果已经形成环路,后面又有插头或者有必过点,则是非法的

            if(left||up||maze[i][j]==2)continue;

            code[j-1]=code[j]=0;

            if(j==M)shift(code,M);

            hm[cur^1].push(encode(code,M),hm[cur].f[k]);

            continue;

        }

        if(left&&up)

        {

            if(left==up)

            {

                code[j-1]=code[j]=0;

                isend=1;

                if(j==M)shift(code,M);

                hm[cur^1].push(encode(code,M),hm[cur].f[k]);

            }

            else //不在一个连通分量则合并

            {

                code[j-1]=code[j]=0;

                for(int t=0;t<=M;t++)

                  if(code[t]==up)

                    code[t]=left;

                if(j==M)shift(code,M);

                hm[cur^1].push(encode(code,M),hm[cur].f[k]);

            }

        }

        else if((left&&(!up))||((!left)&&up))

        {

            int t;

            if(left)t=left;

            else t=up;

            if(maze[i][j+1])

            {

                code[j-1]=0;

                code[j]=t;

                hm[cur^1].push(encode(code,M),hm[cur].f[k]);

            }

            if(maze[i+1][j])

            {

                code[j-1]=t;

                code[j]=0;

                if(j==M)shift(code,M);

                hm[cur^1].push(encode(code,M),hm[cur].f[k]);

            }

        }

        else

        {

            if(maze[i][j+1]&&maze[i+1][j])

            {

                code[j-1]=code[j]=13;

                hm[cur^1].push(encode(code,M),hm[cur].f[k]);

            }

            if(maze[i][j]==1)//可以经过可以不经过的点

            {

                code[j-1]=code[j]=0;

                if(j==M)shift(code,M);

                hm[cur^1].push(encode(code,M),hm[cur].f[k]);

            }

        }

    }

}

void dpblock(int i,int j,int cur)

{

    int k;

    for(k=0;k<hm[cur].size;k++)

    {

        decode(code,M,hm[cur].state[k]);

        code[j-1]=code[j]=0;

        if(j==M)shift(code,M);

        hm[cur^1].push(encode(code,M),hm[cur].f[k]);

    }

}

char str[20];

void init()

{

    scanf("%d%d",&N,&M);

    memset(maze,0,sizeof(maze));

    for(int i=1;i<=N;i++)

    {

        scanf("%s",&str);

        for(int j=1;j<=M;j++)

        {

            if(str[j-1]=='*')maze[i][j]=1;

            else if(str[j-1]=='O')maze[i][j]=2;

        }

    }

}

void solve()

{

    int i,j,cur=0;

    hm[cur].init();

    hm[cur].push(0,1);

    for(int i=1;i<=N;i++)

      for(int j=1;j<=M;j++)

      {

          hm[cur^1].init();

          if(maze[i][j])dpblank(i,j,cur);

          else dpblock(i,j,cur);

          cur^=1;

      }

    long long ans=0;

    for(i=0;i<hm[cur].size;i++)

      ans+=hm[cur].f[i];

    printf("%I64d\n",ans);

}

int main()

{

   // freopen("in.txt","r",stdin);

   // freopen("out.txt","w",stdout);

    int T;

    int iCase=0;

    scanf("%d",&T);

    while(T--)

    {

        iCase++;

        printf("Case %d: ",iCase);

        init();

        solve();

    }

    return 0;

}

 

 

 

 

HDU 1964 Pipes  每个格子之间的墙壁有个花费,求用一个环经过每个格子仅一次的最小花费。此题不求方案数,只需要取最小值即可。而且所有格子都是非障碍格子,这样和Ural 1519就和类似了,而且更加简单。

解题报告

http://www.cnblogs.com/kuangbin/archive/2012/09/30/2709372.html

 

HDU 1964
/*

HDU 1964 Pipes

插头DP

每个格子之间的墙壁有一个花费,求用一个环经过每个格子一次的最小花费

G++ 46ms

*/



#include<stdio.h>

#include<iostream>

#include<string.h>

#include<algorithm>

using namespace std;

const int MAXD=15;

const int HASH=10007;

const int STATE=1000010;



struct Node

{

    int down,right;//每个格子下边和右边墙的花费

}node[MAXD][MAXD];

int N,M;

int maze[MAXD][MAXD];

int code[MAXD];

int ch[MAXD];//最小表示法使用

int ex,ey;//最后一个非障碍格子的坐标



struct HASHMAP

{

    int head[HASH],next[STATE],size;

    int dp[STATE];

    long long state[STATE];//最小表示法,最大是8^11,就是2^33,所以用long long

    void init()

    {

        size=0;

        memset(head,-1,sizeof(head));

    }

    void push(long long st,int ans)

    {

        int i,h=st%HASH;

        for(i=head[h];i!=-1;i=next[i])

           if(state[i]==st)

           {

               if(dp[i]>ans)dp[i]=ans;

               return;

           }

        dp[size]=ans;

        state[size]=st;

        next[size]=head[h];

        head[h]=size++;

    }

}hm[2];

void decode(int *code,int m,long long st)

{

    for(int i=m;i>=0;i--)

    {

        code[i]=st&7;

        st>>=3;

    }

}

long long encode(int *code,int m)

{

    int cnt=1;

    memset(ch,-1,sizeof(ch));

    ch[0]=0;

    long long st=0;

    for(int i=0;i<=m;i++)

    {

        if(ch[code[i]]==-1)ch[code[i]]=cnt++;

        code[i]=ch[code[i]];

        st<<=3;

        st|=code[i];

    }

    return st;

}

void shift(int *code,int m)

{

    for(int i=m;i>0;i--)code[i]=code[i-1];

    code[0]=0;

}

void dpblank(int i,int j,int cur)

{

    int k,left,up;

    for(k=0;k<hm[cur].size;k++)

    {

        decode(code,M,hm[cur].state[k]);

        left=code[j-1];

        up=code[j];

        if(left&&up)

        {

            if(left==up)//只出现在最后一个格子

            {

                if(i==ex&&j==ey)

                {

                    code[j-1]=code[j]=0;

                    if(j==M)shift(code,M);

                    hm[cur^1].push(encode(code,M),hm[cur].dp[k]);

                }

            }

            else

            {

                code[j-1]=code[j]=0;

                for(int t=0;t<=M;t++)

                    if(code[t]==left)

                         code[t]=up;

                if(j==M)shift(code,M);

                hm[cur^1].push(encode(code,M),hm[cur].dp[k]);

            }

        }

        else if((left&&(!up))||((!left)&&up))

        {

            int t;

            if(left)t=left;

            else t=up;

            if(maze[i][j+1])

            {

                code[j-1]=0;

                code[j]=t;

                hm[cur^1].push(encode(code,M),hm[cur].dp[k]+node[i][j].right);

            }

            if(maze[i+1][j])

            {

                code[j-1]=t;

                code[j]=0;

                if(j==M)shift(code,M);

                hm[cur^1].push(encode(code,M),hm[cur].dp[k]+node[i][j].down);

            }

        }

        else//无插头

        {

            if(maze[i][j+1]&&maze[i+1][j])

            {

                code[j-1]=code[j]=13;

                hm[cur^1].push(encode(code,M),hm[cur].dp[k]+node[i][j].down+node[i][j].right);

            }

        }

    }

}

char str[30];

void init()

{

    scanf("%d%d%*c",&N,&M);//跳过一个字符

    memset(maze,0,sizeof(maze));

    for(int i=1;i<=N;i++)

      for(int j=1;j<=M;j++)

          maze[i][j]=1;

    gets(str);

    for(int i=1;i<N;i++)

    {

        gets(str);

        for(int j=1;j<M;j++)

          node[i][j].right=str[2*j]-'0';

        gets(str);

        for(int j=1;j<=M;j++)

          node[i][j].down=str[2*j-1]-'0';

    }

    gets(str);

    for(int j=1;j<M;j++)

        node[N][j].right=str[2*j]-'0';

    gets(str);

    ex=N;

    ey=M;

}

void solve()

{

    int i,j,cur=0;

    int ans=0;

    hm[cur].init();

    hm[cur].push(0,0);

    for(i=1;i<=N;i++)

      for(j=1;j<=M;j++)

      {

          hm[cur^1].init();

          dpblank(i,j,cur);

          cur^=1;

      }

    for(i=0;i<hm[cur].size;i++)

        ans+=hm[cur].dp[i];

    printf("%d\n",ans);

}

int main()

{

   // freopen("in.txt","r",stdin);

   // freopen("out.txt","w",stdout);

    int T;

    scanf("%d",&T);

    while(T--)

    {

        init();

        solve();

    }

    return 0;

}

 

 

HDU 3377  Plan    从左上角走到右下角,每个格子有个分数。每个格子最多经过一次,可以不经过,求最大分数。 模板修改下就出来了。

解题报告

http://www.cnblogs.com/kuangbin/archive/2012/10/01/2709773.html

HDU 3377
/*

HDU 3377

从左上角走到右下角。每个格子有个分数。

每个格子只能经过一次,可以不经过

求最大分数

G++ 140ms

*/

#include<stdio.h>

#include<iostream>

#include<algorithm>

#include<string.h>

using namespace std;



const int MAXD=15;

const int HASH=10007;

const int STATE=1000010;



int N,M;

int maze[MAXD][MAXD];

int score[MAXD][MAXD];

int code[MAXD];

int ch[MAXD];



struct HASHMAP

{

    int head[HASH],state[STATE],next[STATE],size;

    int dp[STATE];

    void init()

    {

        size=0;

        memset(head,-1,sizeof(head));

    }

    void push(int st,int ans)

    {

        int i,h=st%HASH;

        for(i=head[h];i!=-1;i=next[i])

           if(state[i]==st)

           {

               if(dp[i]<ans)dp[i]=ans;

               return;

           }

        state[size]=st;

        dp[size]=ans;

        next[size]=head[h];

        head[h]=size++;

    }

}hm[2];

void decode(int *code,int m,int st)

{

    for(int i=m;i>=0;i--)

    {

        code[i]=st&7;

        st>>=3;

    }

}

int encode(int *code,int m)

{

    int cnt=1;

    memset(ch,-1,sizeof(ch));

    ch[0]=0;

    int st=0;

    for(int i=0;i<=m;i++)

    {

        if(ch[code[i]]==-1)ch[code[i]]=cnt++;

        code[i]=ch[code[i]];

        st<<=3;

        st|=code[i];

    }

    return st;

}

void shift(int *code,int m)

{

    for(int i=m;i>0;i--)code[i]=code[i-1];

    code[0]=0;

}



void dpblank(int i,int j,int cur)

{

    int k,left,up;

    for(k=0;k<hm[cur].size;k++)

    {

        decode(code,M,hm[cur].state[k]);

        left=code[j-1];

        up=code[j];

        if((i==1&&j==1)||(i==N&&j==M))

        {

            if((left&&(!up))||((!left)&&up))

            {

                code[j-1]=code[j]=0;

                if(j==M)shift(code,M);

                hm[cur^1].push(encode(code,M),hm[cur].dp[k]+score[i][j]);

            }

            else if(left==0&&up==0)

            {

                if(maze[i][j+1])

                {

                    code[j-1]=0;

                    code[j]=13;

                    hm[cur^1].push(encode(code,M),hm[cur].dp[k]+score[i][j]);

                }

                if(maze[i][j+1])

                {

                    code[j-1]=13;

                    code[j]=0;

                    if(j==M)shift(code,M);

                    hm[cur^1].push(encode(code,M),hm[cur].dp[k]+score[i][j]);

                }

            }

            continue;

        }

        if(left&&up)

        {

            if(left==up)//没有这种情况,因为不形成环

            {



            }

            else

            {

                code[j-1]=code[j]=0;

                for(int t=0;t<=M;t++)//这里少了个等号,查了好久的错

                  if(code[t]==up)

                    code[t]=left;

                if(j==M)shift(code,M);

                hm[cur^1].push(encode(code,M),hm[cur].dp[k]+score[i][j]);

            }

        }

        else if((left&&(!up))||((!left)&&up))

        {

            int t;

            if(left)t=left;

            else t=up;

            if(maze[i][j+1])

            {

                code[j-1]=0;

                code[j]=t;

                hm[cur^1].push(encode(code,M),hm[cur].dp[k]+score[i][j]);

            }

            if(maze[i+1][j])

            {

                code[j]=0;

                code[j-1]=t;

                if(j==M)shift(code,M);

                hm[cur^1].push(encode(code,M),hm[cur].dp[k]+score[i][j]);

            }

        }

        else

        {

            if(maze[i][j+1]&&maze[i+1][j])

            {

                code[j]=code[j-1]=13;

                hm[cur^1].push(encode(code,M),hm[cur].dp[k]+score[i][j]);

            }

            code[j-1]=code[j]=0;

            if(j==M)shift(code,M);

            hm[cur^1].push(encode(code,M),hm[cur].dp[k]);

        }

    }

}



void init()

{

    memset(maze,0,sizeof(maze));

    for(int i=1;i<=N;i++)

      for(int j=1;j<=M;j++)

      {

          maze[i][j]=1;

          scanf("%d",&score[i][j]);

      }

}

void solve()

{

    int i,j,cur=0;

    hm[cur].init();

    hm[cur].push(0,0);

    for(int i=1;i<=N;i++)

      for(int j=1;j<=M;j++)

      {

          hm[cur^1].init();

          dpblank(i,j,cur);

          cur^=1;

      }

    int ans=0;

    for(int i=0;i<hm[cur].size;i++)

       ans+=hm[cur].dp[i];

    printf("%d\n",ans);

}

int main()

{

   // freopen("in.txt","r",stdin);

   // freopen("out.txt","w",stdout);

    int iCase=0;

    while(scanf("%d%d",&N,&M)!=EOF)

    {

        iCase++;

        printf("Case %d: ",iCase);

        init();

        if(N==1&&M==1)

        {

            printf("%d\n",score[1][1]);

            continue;

        }

        solve();

    }

    return 0;

}



/*

Sample Input

2 2

1 2

3 1

3 3

0 -20 100

1 -20 -20

1   1   1





Sample Output

Case 1: 5

Case 2: 61



*/

 

 

 

 

POJ  1739  Tony's Tour   楼教主的男人八题之一。从左下角走到右下角,每个非障碍格子仅走一次的方法数。一种方法是在后面增加两行转换成回路问题,这样就和ural 1519一样了。也可以不增加行,直接特殊处理下即可。

解题报告

http://www.cnblogs.com/kuangbin/archive/2012/09/30/2709114.html

POJ 1739
/*

POJ 1739

题目意思就是从左下角走到右下角,每个非障碍格子都走一遍的方法数

转换成回路问题。

在最后加两行

   .########.

   ..........

这样就转成回路问题了,就和URAL 1519 一样的做法了



G++ 47ms

*/

#include<iostream>

#include<string.h>

#include<algorithm>

#include<stdio.h>

using namespace std;



const int MAXD=15;

const int HASH=10007;

const int STATE=1000010;



int N,M;

int maze[MAXD][MAXD];

int code[MAXD];

int ch[MAXD];//最小表示法使用

int ex,ey;//最后一个非障碍格子的坐标



struct HASHMAP

{

    int head[HASH],next[STATE],size;

    long long state[STATE],f[STATE];

    void init()

    {

        size=0;

        memset(head,-1,sizeof(head));

    }

    void push(long long st,long long ans)

    {

        int i;

        int h=st%HASH;

        for(i=head[h];i!=-1;i=next[i])

          if(state[i]==st)

          {

              f[i]+=ans;

              return;

          }

        state[size]=st;

        f[size]=ans;

        next[size]=head[h];

        head[h]=size++;

    }

}hm[2];



void decode(int *code,int m,long long st)

{

    for(int i=m;i>=0;i--)

    {

        code[i]=st&7;

        st>>=3;

    }

}

long long encode(int *code,int m)

{

    int cnt=1;

    memset(ch,-1,sizeof(ch));

    ch[0]=0;

    long long st=0;

    for(int i=0;i<=m;i++)

    {

        if(ch[code[i]]==-1)ch[code[i]]=cnt++;

        code[i]=ch[code[i]];

        st<<=3;

        st|=code[i];

    }

    return st;

}

void shift(int *code,int m)

{

    for(int i=m;i>0;i--)code[i]=code[i-1];

    code[0]=0;

}



void dpblank(int i,int j,int cur)

{

    int k,left,up;

    for(k=0;k<hm[cur].size;k++)

    {

        decode(code,M,hm[cur].state[k]);

        left=code[j-1];

        up=code[j];

        if(left&&up)

        {

            if(left==up)

            {

                if(i==ex&&j==ey)//只能出现在最后一个非障碍格子

                {

                    code[j]=code[j-1]=0;

                    if(j==M)shift(code,M);

                    hm[cur^1].push(encode(code,M),hm[cur].f[k]);

                }

            }

            else //不在同一个连通分量则合并

            {

                code[j-1]=code[j]=0;

                for(int t=0;t<=M;t++)

                  if(code[t]==left)

                    code[t]=up;

                if(j==M)shift(code,M);

                hm[cur^1].push(encode(code,M),hm[cur].f[k]);

            }

        }

        else if((left&&(!up))||((!left)&&up))

        {

            int t;

            if(left)t=left;

            else t=up;

            if(maze[i][j+1])

            {

                code[j-1]=0;

                code[j]=t;

                hm[cur^1].push(encode(code,M),hm[cur].f[k]);

            }

            if(maze[i+1][j])

            {

                code[j-1]=t;

                code[j]=0;

                if(j==M)shift(code,M);

                hm[cur^1].push(encode(code,M),hm[cur].f[k]);

            }

        }

        else

        {

            if(maze[i][j+1]&&maze[i+1][j])

            {

                code[j-1]=code[j]=13;

                hm[cur^1].push(encode(code,M),hm[cur].f[k]);

            }

        }

    }

}

void dpblock(int i,int j,int cur)

{

    int k;

    for(k=0;k<hm[cur].size;k++)

    {

        decode(code,M,hm[cur].state[k]);

        code[j-1]=code[j]=0;

        if(j==M)shift(code,M);

        hm[cur^1].push(encode(code,M),hm[cur].f[k]);

    }

}

char str[20];

void init()

{

    memset(maze,0,sizeof(maze));

    for(int i=1;i<=N;i++)

    {

        scanf("%s",&str);

        for(int j=0;j<M;j++)

          if(str[j]=='.')

          {

              maze[i][j+1]=1;

          }

    }

    maze[N+1][1]=maze[N+1][M]=1;

    for(int i=2;i<M;i++)maze[N+1][i]=0;

    for(int i=1;i<=M;i++)maze[N+2][i]=1;

    N+=2;

    ex=N,ey=M;

}

void solve()

{

    int i,j,cur=0;

    hm[cur].init();

    hm[cur].push(0,1);

    for(i=1;i<=N;i++)

      for(j=1;j<=M;j++)

      {

          hm[cur^1].init();

          if(maze[i][j])dpblank(i,j,cur);

          else dpblock(i,j,cur);

          cur^=1;

      }

    long long ans=0;

    for(int i=0;i<hm[cur].size;i++)

      ans+=hm[cur].f[i];

    printf("%I64d\n",ans);

}

int main()

{

    //freopen("in.txt","r",stdin);

    //freopen("out.txt","w",stdout);

    while(scanf("%d%d",&N,&M))

    {

        if(N==0&&M==0)break;

        init();

        solve();

    }

    return 0;

}

 

POJ 1739
/*

POJ 1739

不增加行。

起点和终点特殊处理



G++ 47ms

*/

#include<iostream>

#include<string.h>

#include<algorithm>

#include<stdio.h>

using namespace std;



const int MAXD=15;

const int HASH=10007;

const int STATE=1000010;



int N,M;

int maze[MAXD][MAXD];

int code[MAXD];

int ch[MAXD];//最小表示法使用

int ex,ey;//最后一个非障碍格子的坐标



struct HASHMAP

{

    int head[HASH],next[STATE],size;

    long long state[STATE],f[STATE];

    void init()

    {

        size=0;

        memset(head,-1,sizeof(head));

    }

    void push(long long st,long long ans)

    {

        int i;

        int h=st%HASH;

        for(i=head[h];i!=-1;i=next[i])

          if(state[i]==st)

          {

              f[i]+=ans;

              return;

          }

        state[size]=st;

        f[size]=ans;

        next[size]=head[h];

        head[h]=size++;

    }

}hm[2];



void decode(int *code,int m,long long st)

{

    for(int i=m;i>=0;i--)

    {

        code[i]=st&7;

        st>>=3;

    }

}

long long encode(int *code,int m)

{

    int cnt=1;

    memset(ch,-1,sizeof(ch));

    ch[0]=0;

    long long st=0;

    for(int i=0;i<=m;i++)

    {

        if(ch[code[i]]==-1)ch[code[i]]=cnt++;

        code[i]=ch[code[i]];

        st<<=3;

        st|=code[i];

    }

    return st;

}

void shift(int *code,int m)

{

    for(int i=m;i>0;i--)code[i]=code[i-1];

    code[0]=0;

}



void dpblank(int i,int j,int cur)

{

    int k,left,up;

    for(k=0;k<hm[cur].size;k++)

    {

        decode(code,M,hm[cur].state[k]);

        left=code[j-1];

        up=code[j];



        if((i==N&&j==1)||(i==N&&j==M))//起点和终点

        {

            if((left&&(!up))||((!left)&&up))

            {

                code[j]=code[j-1]=0;

                if(j==M)shift(code,M);

                hm[cur^1].push(encode(code,M),hm[cur].f[k]);

            }

            else if(left==0&&up==0)

            {

                if(maze[i][j+1])

                {

                   code[j-1]=0;

                   code[j]=13;

                   hm[cur^1].push(encode(code,M),hm[cur].f[k]);

                }

                if(maze[i+1][j])

                {

                   code[j-1]=13;

                   code[j]=0;

                   if(j==M)shift(code,M);

                   hm[cur^1].push(encode(code,M),hm[cur].f[k]);

                }

            }

            continue;

        }

        if(left&&up)

        {

            if(left==up)

            {//这种情况不能发生

               /* if(i==ex&&j==ey)//只能出现在最后一个非障碍格子

                {

                    code[j]=code[j-1]=0;

                    if(j==M)shift(code,M);

                    hm[cur^1].push(encode(code,M),hm[cur].f[k]);

                }*/

            }

            else //不在同一个连通分量则合并

            {

                code[j-1]=code[j]=0;

                for(int t=0;t<=M;t++)

                  if(code[t]==left)

                    code[t]=up;

                if(j==M)shift(code,M);

                hm[cur^1].push(encode(code,M),hm[cur].f[k]);

            }

        }

        else if((left&&(!up))||((!left)&&up))

        {

            int t;

            if(left)t=left;

            else t=up;

            if(maze[i][j+1])

            {

                code[j-1]=0;

                code[j]=t;

                hm[cur^1].push(encode(code,M),hm[cur].f[k]);

            }

            if(maze[i+1][j])

            {

                code[j-1]=t;

                code[j]=0;

                if(j==M)shift(code,M);

                hm[cur^1].push(encode(code,M),hm[cur].f[k]);

            }

        }

        else

        {

            if(maze[i][j+1]&&maze[i+1][j])

            {

                code[j-1]=code[j]=13;

                hm[cur^1].push(encode(code,M),hm[cur].f[k]);

            }

        }

    }

}

void dpblock(int i,int j,int cur)

{

    int k;

    for(k=0;k<hm[cur].size;k++)

    {

        decode(code,M,hm[cur].state[k]);

        code[j-1]=code[j]=0;

        if(j==M)shift(code,M);

        hm[cur^1].push(encode(code,M),hm[cur].f[k]);

    }

}

char str[20];

void init()

{

    memset(maze,0,sizeof(maze));

    for(int i=1;i<=N;i++)

    {

        scanf("%s",&str);

        for(int j=0;j<M;j++)

          if(str[j]=='.')

          {

              maze[i][j+1]=1;

          }

    }

}

void solve()

{

    int i,j,cur=0;

    hm[cur].init();

    hm[cur].push(0,1);

    for(i=1;i<=N;i++)

      for(j=1;j<=M;j++)

      {

          hm[cur^1].init();

          if(maze[i][j])dpblank(i,j,cur);

          else dpblock(i,j,cur);

          cur^=1;

      }

    long long ans=0;

    for(int i=0;i<hm[cur].size;i++)

      ans+=hm[cur].f[i];

    printf("%I64d\n",ans);

}

int main()

{

    //freopen("in.txt","r",stdin);

   // freopen("out.txt","w",stdout);

    while(scanf("%d%d",&N,&M))

    {

        if(N==0&&M==0)break;

        init();

        solve();

    }

    return 0;

}

 

 

 

 

POJ  3133  Manhattan Wiring   格子中有两个2,两个3.求把两个2连起来,两个3连起来。求经过总的格子数的总和减2.  两条路径不能交叉。有障碍格子。非障碍格子最多经过一次。插头出需要记录三种状态:没有插头、2号插头、3号插头。可以用三进制。但是考虑到4进制更加高效,我用的四进制做的。

解题报告

http://www.cnblogs.com/kuangbin/archive/2012/09/30/2709263.html

 

POJ 3133
/*

POJ 3133

连接2的插头为2,连接3的插头为3

没有插头为0

用四进制表示(四进制比三进制高效)



G++ 391ms

*/

#include<stdio.h>

#include<iostream>

#include<string.h>

#include<algorithm>

using namespace std;

const int MAXD=15;

const int HASH=10007;

const int STATE=1000010;

int N,M;

int maze[MAXD][MAXD];//0表示障碍,1是非障碍,2和3

int code[MAXD];

//0表示没有插头,2表示和插头2相连,3表示和插头3相连



struct HASHMAP

{

    int head[HASH],next[STATE],size;

    int state[STATE];

    int dp[STATE];

    void init()

    {

        size=0;

        memset(head,-1,sizeof(head));

    }

    void push(int st,int ans)

    {

        int i,h=st%HASH;

        for(i=head[h];i!=-1;i=next[i])

          if(state[i]==st)

          {

              if(dp[i]>ans)dp[i]=ans;

              return;

          }

        state[size]=st;

        dp[size]=ans;

        next[size]=head[h];

        head[h]=size++;

    }

}hm[2];

void decode(int *code,int m,int st)//四进制

{

    int i;

    for(int i=m;i>=0;i--)

    {

        code[i]=(st&3);

        st>>=2;

    }

}

int encode(int *code,int m)

{

    int i;

    int st=0;

    for(int i=0;i<=m;i++)

    {

        st<<=2;

        st|=code[i];

    }

    return st;

}

void shift(int *code,int m)//换行

{

    for(int i=m;i>0;i--)code[i]=code[i-1];

    code[0]=0;

}

void dpblank(int i,int j,int cur)

{

    int k,left,up;

    for(k=0;k<hm[cur].size;k++)

    {

        decode(code,M,hm[cur].state[k]);

        left=code[j-1];

        up=code[j];

        if(left&&up)

        {

            if(left==up)//只能是相同的插头

            {

                code[j-1]=code[j]=0;

                if(j==M)shift(code,M);

                hm[cur^1].push(encode(code,M),hm[cur].dp[k]+1);

            }

        }

        else if((left&&(!up))||((!left)&&up))//只有一个插头

        {

            int t;

            if(left)t=left;

            else t=up;//这里少写个else ,查了好久的错误

            if(maze[i][j+1]==1||maze[i][j+1]==t)//插头从右边出来

            {

                code[j-1]=0;

                code[j]=t;

                hm[cur^1].push(encode(code,M),hm[cur].dp[k]+1);

            }

            if(maze[i+1][j]==1||maze[i+1][j]==t)//插头从下边出来

            {

                code[j]=0;

                code[j-1]=t;

                if(j==M)shift(code,M);

                hm[cur^1].push(encode(code,M),hm[cur].dp[k]+1);

            }

        }

        else if(left==0&&up==0)//没有插头

        {

            code[j-1]=code[j]=0;//不加插头

            if(j==M)shift(code,M);

            hm[cur^1].push(encode(code,M),hm[cur].dp[k]);

            if(maze[i][j+1]&&maze[i+1][j])

            {

                if(maze[i][j+1]==1&&maze[i+1][j]==1)

                {

                    decode(code,M,hm[cur].state[k]);

                    code[j-1]=code[j]=2;//加2号插头

                    hm[cur^1].push(encode(code,M),hm[cur].dp[k]+1);

                    //decode(code,M,hm[cur].state[k]);

                    code[j-1]=code[j]=3;//加3号插头

                    hm[cur^1].push(encode(code,M),hm[cur].dp[k]+1);

                }

                else if((maze[i][j+1]==2&&maze[i+1][j]==1)||(maze[i+1][j]==2&&maze[i][j+1]==1)||(maze[i][j+1]==2&&maze[i+1][j]==2))

                {

                    decode(code,M,hm[cur].state[k]);

                    code[j-1]=code[j]=2;

                    hm[cur^1].push(encode(code,M),hm[cur].dp[k]+1);

                }

                else if((maze[i][j+1]==3&&maze[i+1][j]==1)||(maze[i+1][j]==3&&maze[i][j+1]==1)||(maze[i][j+1]==3&&maze[i+1][j]==3))

                {

                    decode(code,M,hm[cur].state[k]);

                    code[j-1]=code[j]=3;

                    hm[cur^1].push(encode(code,M),hm[cur].dp[k]+1);

                }

            }

        }

    }

}

void dpblock(int i,int j,int cur)

{

    int k;

    for(k=0;k<hm[cur].size;k++)

    {

        decode(code,M,hm[cur].state[k]);

        if(code[j-1]!=0||code[j]!=0)continue;

        code[j-1]=code[j]=0;//不加插头

        if(j==M)shift(code,M);

        hm[cur^1].push(encode(code,M),hm[cur].dp[k]);

    }

}

void dp_2(int i,int j,int cur)

{

    int left,up,k;

    for(k=0;k<hm[cur].size;k++)

    {

        decode(code,M,hm[cur].state[k]);

        left=code[j-1];

        up=code[j];

        if((left==2&&up==0)||(left==0&&up==2))

        {

            code[j-1]=code[j]=0;

            if(j==M)shift(code,M);

            hm[cur^1].push(encode(code,M),hm[cur].dp[k]+1);

        }

        else if(left==0&&up==0)

        {

            if(maze[i][j+1]==1||maze[i][j+1]==2)

            {

                code[j-1]=0;

                code[j]=2;

                hm[cur^1].push(encode(code,M),hm[cur].dp[k]+1);

            }

            if(maze[i+1][j]==1||maze[i+1][j]==2)

            {

                code[j-1]=2;

                code[j]=0;

                if(j==M)shift(code,M);

                hm[cur^1].push(encode(code,M),hm[cur].dp[k]+1);

            }

        }

    }

}

void dp_3(int i,int j,int cur)

{

    int left,up,k;

    for(k=0;k<hm[cur].size;k++)

    {

        decode(code,M,hm[cur].state[k]);

        left=code[j-1];

        up=code[j];

        if((left==3&&up==0)||(left==0&&up==3))

        {

            code[j-1]=code[j]=0;

            if(j==M)shift(code,M);

            hm[cur^1].push(encode(code,M),hm[cur].dp[k]+1);

        }

        else if(left==0&&up==0)

        {

            if(maze[i][j+1]==1||maze[i][j+1]==3)

            {

                code[j-1]=0;

                code[j]=3;

                hm[cur^1].push(encode(code,M),hm[cur].dp[k]+1);

            }

            if(maze[i+1][j]==1||maze[i+1][j]==3)

            {

                code[j-1]=3;

                code[j]=0;

                if(j==M)shift(code,M);

                hm[cur^1].push(encode(code,M),hm[cur].dp[k]+1);

            }

        }

    }

}



void init()

{

    memset(maze,0,sizeof(maze));

    for(int i=1;i<=N;i++)

      for(int j=1;j<=M;j++)

      {

          scanf("%d",&maze[i][j]);

          //if(maze[i][j]==0)maze[i][j]=1;

          //if(maze[i][j]==1)maze[i][j]=0;

          //上面的写法是错的,!!!

          if(maze[i][j]==1||maze[i][j]==0)maze[i][j]^=1;//0变1,1变0

      }

}

void solve()

{

    int i,j,cur=0;

    hm[cur].init();

    hm[cur].push(0,0);

    for(int i=1;i<=N;i++)

      for(int j=1;j<=M;j++)

      {

          hm[cur^1].init();

          if(maze[i][j]==0)dpblock(i,j,cur);

          else if(maze[i][j]==1)dpblank(i,j,cur);

          else if(maze[i][j]==2)dp_2(i,j,cur);

          else if(maze[i][j]==3)dp_3(i,j,cur);

          cur^=1;

      }

    int ans=0;

    for(int i=0;i<hm[cur].size;i++)

      ans+=hm[cur].dp[i];

    if(ans>0)ans-=2;

    printf("%d\n",ans);

}

int main()

{

    //freopen("in.txt","r",stdin);

    //freopen("out.txt","w",stdout);

    while(scanf("%d%d",&N,&M))

    {

        if(N==0&&M==0)break;

        init();

        solve();

    }

    return 0;

}

 

 

 

ZOJ  3466 The Hive II  和HDU 1693一样,求多回路问题。但是四边形换成了六边行。难度相应提高了。做的方法是一样的。我是倒过来,按照列来转移的,稍微简单一下。按照行可以做,但是讨论比较多,我没有去尝试。

解题报告

http://www.cnblogs.com/kuangbin/archive/2012/09/29/2708909.html

ZOJ 3466
/*

ZOJ 3466

C++ 3850ms 62768K

原来ZOJ中的long long要用%lld输出啊

对ZOJ不熟悉啊,被坑了好久



*/



#include<stdio.h>

#include<string.h>

#include<algorithm>

#include<iostream>

using namespace std;



const int HASH=10007;

const int STATE=2000010;

const int MAXD=32;

int N,M;

int code[MAXD],maze[MAXD][MAXD];



struct HASHMAP

{

    int head[HASH],next[STATE],state[STATE],size;

    long long f[STATE];

    void init()

    {

        size=0;

        memset(head,-1,sizeof(head));

    }

    void push(int st,long long ans)

    {

        int i,h=st%HASH;

        for(i=head[h];i!=-1;i=next[i])

          if(st==state[i])

          {

              f[i]+=ans;

              return;

          }

        f[size]=ans;

        state[size]=st;

        next[size]=head[h];

        head[h]=size++;

    }

}hm[2];

void decode(int *code,int m,int st)

{

    int i;

    for(i=m;i>=0;i--)

    {

        code[i]=st&1;

        st>>=1;

    }

}

int encode(int *code,int m)

{

    int i,st=0;

    for(i=0;i<=m;i++)

    {

        st<<=1;

        st|=code[i];

    }

    return st;

}

void init()

{

    N=8;//倒过来,8行

    int t;

    scanf("%d",&t);

    memset(maze,0,sizeof(maze));

    for(int i=1;i<=N;i++)

      for(int j=1;j<=M;j++)

        maze[i][j]=1;

    char str[10];

    while(t--)

    {

        scanf("%s",&str);

        maze[str[1]-'A'+1][M-(str[0]-'A')]=0;

    }

}

void shift(int *code,int m)//偶数行换奇数行的时候要变化

{

    for(int i=m;i>1;i--)

      code[i]=code[i-2];

    code[0]=0;

    code[1]=0;

}

void dpblank(int i,int j,int cur)

{

    int k,left,up1,up2;

    int t1,t2;

    if(i%2==0)t1=j,t2=j+1;

    else t1=j-1,t2=j;

    for(k=0;k<hm[cur].size;k++)

    {

        decode(code,2*M,hm[cur].state[k]);

        left=code[2*(j-1)];

        up1=code[2*j-1];

        up2=code[2*j];

       // printf("%d %d:  %d  %d  %d\n",i,j,left,up1,up2);

        if((left==1&&up1==1&&up2==0)||(left==0&&up1==1&&up2==1)||(left==1&&up1==0&&up2==1))

        {

            code[2*j-2]=code[2*j-1]=code[2*j]=0;

            if(j==M&&i%2==0)shift(code,2*M);

            hm[cur^1].push(encode(code,2*M),hm[cur].f[k]);

        }

        else if((left==1&&up1==0&&up2==0)||(left==0&&up1==1&&up2==0)||(left==0&&up1==0&&up2==1))

        {

            if(maze[i+1][t1])

            {

                code[2*j-2]=1;

                code[2*j-1]=code[2*j]=0;

                if(j==M&&i%2==0)shift(code,2*M);

                hm[cur^1].push(encode(code,2*M),hm[cur].f[k]);

            }

            if(maze[i+1][t2])

            {

                code[2*j-2]=code[2*j]=0;

                code[2*j-1]=1;

                hm[cur^1].push(encode(code,2*M),hm[cur].f[k]);

            }

            if(maze[i][j+1])

            {

                code[2*j-2]=code[2*j-1]=0;

                code[2*j]=1;

                hm[cur^1].push(encode(code,2*M),hm[cur].f[k]);

            }

        }

        else if(left==0&&up1==0&&up2==0)

        {

            if(maze[i+1][t1]&&maze[i+1][t2])

            {

                code[2*j-2]=code[2*j-1]=1;

                code[2*j]=0;

                hm[cur^1].push(encode(code,2*M),hm[cur].f[k]);

            }

            if(maze[i+1][t1]&&maze[i][j+1])

            {

                code[2*j-2]=code[2*j]=1;

                code[2*j-1]=0;

                hm[cur^1].push(encode(code,2*M),hm[cur].f[k]);

            }

            if(maze[i+1][t2]&&maze[i][j+1])

            {

                code[2*j-2]=0;

                code[2*j-1]=code[2*j]=1;

                hm[cur^1].push(encode(code,2*M),hm[cur].f[k]);

            }

        }

    }

}

void dpblock(int i,int j,int cur)

{

    int k;

    for(k=0;k<hm[cur].size;k++)

    {

        decode(code,2*M,hm[cur].state[k]);

        code[2*j-2]=code[2*j-1]=code[2*j]=0;

        if(j==M&&i%2==0)shift(code,2*M);

        hm[cur^1].push(encode(code,2*M),hm[cur].f[k]);

    }

}

void solve()

{

    int i,j,cur=0;

    long long ans=0;

    hm[cur].init();

    hm[cur].push(0,1);

    for(i=1;i<=N;i++)

      for(j=1;j<=M;j++)

      {

          hm[cur^1].init();

          if(maze[i][j])dpblank(i,j,cur);

          else dpblock(i,j,cur);

          cur^=1;

      }

    for(i=0;i<hm[cur].size;i++)

      if(hm[cur].state[i]==0)

         ans+=hm[cur].f[i];

    printf("%lld\n",ans);//ZOJ中的C++要lld才能AC的

}

int main()

{

    //freopen("in.txt","r",stdin);

    //freopen("out.txt","w",stdout);

    while(scanf("%d",&M)!=EOF)

    {

        init();

        solve();

    }

    return 0;

}

 

 

 

 

 

ZOJ  3256  Tour in the Castle   这题数据很大。求左上角到右下角的路径数。经过所以格子一次。要用矩阵加速。由于每一列都是一样的,状态转移就相同。用插头DP求出列与列之间的状态转移,然后用矩阵乘法。这题比较难

解题报告

http://www.cnblogs.com/kuangbin/archive/2012/10/01/2709831.html

ZOJ 3256
/*

ZOJ 3256

N*M(2<=N<=7,1<=M<=10^9)的方格,问从左上角的格子到左下角的格子,

而且仅经过所有格子一次的路径数

插头DP+矩阵加速



对于一个图的邻接矩阵的N次方,其中(i,j)位置上的元素表示

点i经过N步到达点j的方案数

*/

#include<stdio.h>

#include<iostream>

#include<algorithm>

#include<string.h>

using namespace std;



const int STATE=1010;

const int HASH=419;//这个小一点,效率高

const int MOD=7777777;



int N,M;

int D;

int code[10];

int ch[10];

int g[200][200];//状态转移图



struct Matrix

{

    int n,m;

    int mat[200][200];

};

Matrix mul(Matrix a,Matrix b)//矩阵相乘,要保证a的列数和b的行数相等

{

    Matrix ret;

    ret.n=a.n;

    ret.m=b.m;

    long long sum;

    for(int i=0;i<a.n;i++)

       for(int j=0;j<b.m;j++)

       {

           sum=0;

           for(int k=0;k<a.m;k++)

           {

               sum+=(long long)a.mat[i][k]*b.mat[k][j];

               //sum%=MOD;//加了这句话就会TLE,坑啊。。。

           }

           ret.mat[i][j]=sum%MOD;

       }

    return ret;

}

Matrix pow_M(Matrix a,int n)//方阵的n次方

{

    Matrix ret=a;

    memset(ret.mat,0,sizeof(ret.mat));

    for(int i=0;i<a.n;i++)ret.mat[i][i]=1;//单位阵

    Matrix temp=a;

    while(n)

    {

        if(n&1)ret=mul(ret,temp);

        temp=mul(temp,temp);

        n>>=1;

    }

    return ret;

}



struct HASHMAP

{

    int head[HASH],next[STATE],size;

    int state[STATE];

    void init()

    {

        size=0;

        memset(head,-1,sizeof(head));

    }

    int push(int st)

    {

        int i,h=st%HASH;

        for(i=head[h];i!=-1;i=next[i])

           if(state[i]==st)

              return i;

        state[size]=st;

        next[size]=head[h];

        head[h]=size++;

        return size-1;

    }

}hm;

void decode(int *code,int n,int st)

{

    for(int i=n-1;i>=0;i--)

    {

        code[i]=st&3;

        st>>=2;

    }

}

int encode(int *code,int n)

{

    int cnt=1;

    memset(ch,-1,sizeof(ch));

    ch[0]=0;

    int st=0;

    for(int i=0;i<n;i++)

    {

        if(ch[code[i]]==-1)ch[code[i]]=cnt++;

        code[i]=ch[code[i]];

        st<<=2;

        st|=code[i];

    }

    return st;

}



bool check(int st,int nst)//判断两种状态能不能转移

{

    decode(code,N,st);

    int flag=0;//标记格子上边是否有插头

    int cnt=0;

    int k;

    for(int i=0;i<N;i++)

    {

        if(flag==0)//这个格子上边没有插头

        {

            if(code[i]==0&&(nst&(1<<i))==0)//左边和右边都没有插头

               return false;

            if(code[i]&&(nst&(1<<i)))continue;

            if(code[i])flag=code[i];//插头从左边过来,从下边出去

            else flag=-1;//插头从下边进来从右边出去

            k=i;

        }

        else

        {

            if(code[i]&&(nst&(1<<i)))//左边和右边和上边都有插头

               return false;

            if(code[i]==0&&(nst&(1<<i))==0)continue;

            if(code[i])

            {

                if(code[i]==flag&&((nst!=0)||i!=N-1))return false;//只有最后一个格子才能合起来

                if(flag>0)

                {

                    for(int j=0;j<N;j++)

                      if(code[j]==code[i]&&j!=i)

                          code[j]=code[k];

                    code[i]=code[k]=0;

                }

                else

                {

                    code[k]=code[i];

                    code[i]=0;

                }

            }

            else

            {

                if(flag>0)code[i]=code[k],code[k]=0;

                else code[i]=code[k]=N+(cnt++);

            }

            flag=0;

        }

    }

    if(flag!=0)return false;

    return true;

}

struct Node

{

    int g[200][200];

    int D;

}node[20];//打表之用

void init()

{

    if(node[N].D!=0)

    {

        memcpy(g,node[N].g,sizeof(node[N].g));

        D=node[N].D;

        return;

    }

    int st,nst;

    hm.init();

    memset(code,0,sizeof(code));

    code[0]=code[N-1]=1;

    hm.push(0);

    hm.push(encode(code,N));

    memset(g,0,sizeof(g));

    for(int i=1;i<hm.size;i++)

    {

        st=hm.state[i];

        for(nst=0;nst<(1<<N);nst++)

          if(check(st,nst))

          {

              int j=hm.push(encode(code,N));

              g[i][j]=1;

          }

    }

    D=hm.size;

    memcpy(node[N].g,g,sizeof(g));

    node[N].D=D;

}

void solve()

{

    Matrix temp;

    temp.n=temp.m=D;

    memcpy(temp.mat,g,sizeof(g));

    Matrix ans=pow_M(temp,M);

    if(ans.mat[1][0]==0)printf("Impossible\n");

    else printf("%d\n",ans.mat[1][0]%MOD);

}

int main()

{

    //freopen("in.txt","r",stdin);

    //freopen("out.txt","w",stdout);

    for(int i=0;i<20;i++)node[i].D=0;

    while(scanf("%d%d",&N,&M)==2)

    {

        init();

        solve();

    }

    return 0;

}

 

 

 

 

ZOJ  3213  Beautiful Meadow   此题求简单路径。得到最大的分数。

用最小表示法,需要增加标志位记录独立插头个数。要使独立插头个数小于等于2.

解题报告

http://www.cnblogs.com/kuangbin/archive/2012/10/02/2710055.html

ZOJ 3213
/*

ZOJ 3213

*/

#include<stdio.h>

#include<string.h>

#include<algorithm>

#include<iostream>

using namespace std;



const int MAXD=15;

const int HASH=10007;

const int STATE=1000010;



int N,M;

int maze[MAXD][MAXD];

int code[MAXD];

int ch[MAXD];

int num;//独立插头的个数

int ans;//答案



struct HASHMAP

{

    int head[HASH],next[STATE],size;

    int state[STATE],dp[STATE];

    void init()

    {

        size=0;

        memset(head,-1,sizeof(head));

    }

    void push(int st,int ans)

    {

        int i,h=st%HASH;

        for(i=head[h];i!=-1;i=next[i])

          if(state[i]==st)

          {

              if(dp[i]<ans)dp[i]=ans;

              return;

          }

        state[size]=st;

        dp[size]=ans;

        next[size]=head[h];

        head[h]=size++;

    }

}hm[2];

void decode(int *code,int m,int st)

{

    num=st&7;//独立插头个数

    st>>=3;

    for(int i=m;i>=0;i--)

    {

        code[i]=st&7;

        st>>=3;

    }

}

int encode(int *code,int m)

{

    int cnt=1;

    memset(ch,-1,sizeof(ch));

    ch[0]=0;

    int st=0;

    for(int i=0;i<=m;i++)

    {

        if(ch[code[i]]==-1)ch[code[i]]=cnt++;

        code[i]=ch[code[i]];

        st<<=3;

        st|=code[i];

    }

    st<<=3;

    st|=num;

    return st;

}

void shift(int *code,int m)

{

    for(int i=m;i>0;i--)code[i]=code[i-1];

    code[0]=0;

}

void dpblank(int i,int j,int cur)

{

    int k,left,up;

    for(k=0;k<hm[cur].size;k++)

    {

        decode(code,M,hm[cur].state[k]);

        left=code[j-1];

        up=code[j];

        if(left&&up)

        {

            if(left!=up)

            {

                code[j-1]=code[j]=0;

                for(int t=0;t<=M;t++)

                  if(code[t]==up)

                     code[t]=left;

                if(j==M)shift(code,M);

                hm[cur^1].push(encode(code,M),hm[cur].dp[k]+maze[i][j]);

               // hm[cur^1].push(encode(code,j==M?M-1:M),hm[cur].dp[k]+maze[i][j]);

            }

        }

        else if(left||up)

        {

            int t;

            if(left)t=left;

            else t=up;

            if(maze[i][j+1])

            {

                code[j-1]=0;

                code[j]=t;

                hm[cur^1].push(encode(code,M),hm[cur].dp[k]+maze[i][j]);

            }

            if(maze[i+1][j])

            {

                code[j-1]=t;

                code[j]=0;

               hm[cur^1].push(encode(code,j==M?M-1:M),hm[cur].dp[k]+maze[i][j]);

            }

            if(num<2)

            {

                num++;

                code[j-1]=code[j]=0;

                hm[cur^1].push(encode(code,j==M?M-1:M),hm[cur].dp[k]+maze[i][j]);

            }

        }

        else

        {

            code[j-1]=code[j]=0;

           hm[cur^1].push(encode(code,j==M?M-1:M),hm[cur].dp[k]);

            if(maze[i][j+1]&&maze[i+1][j])

            {

                code[j-1]=code[j]=13;

                hm[cur^1].push(encode(code,M),hm[cur].dp[k]+maze[i][j]);

            }

            if(num<2)

            {

                num++;

                if(maze[i][j+1])

                {

                    code[j]=13;

                    code[j-1]=0;

                    hm[cur^1].push(encode(code,M),hm[cur].dp[k]+maze[i][j]);

                }

                if(maze[i+1][j])

                {

                    code[j-1]=13;

                    code[j]=0;

                   hm[cur^1].push(encode(code,j==M?M-1:M),hm[cur].dp[k]+maze[i][j]);

                }

            }

        }

    }

}

void dpblock(int i,int j,int cur)

{

    int k;

    for(k=0;k<hm[cur].size;k++)

    {

        decode(code,M,hm[cur].state[k]);//这个忘记了!!!

        code[j-1]=code[j]=0;

        if(j==M)shift(code,M);

        hm[cur^1].push(encode(code,M),hm[cur].dp[k]);

    }

}

void init()

{

    scanf("%d%d",&N,&M);

    ans=0;

    memset(maze,0,sizeof(maze));//初始化别忘记了

    for(int i=1;i<=N;i++)

      for(int j=1;j<=M;j++)

      {

          scanf("%d",&maze[i][j]);

          if(maze[i][j]>ans)ans=maze[i][j];

      }

}

void solve()

{

    int i,j,cur=0;

    hm[cur].init();

    hm[cur].push(0,0);

    for(i=1;i<=N;i++)

       for(int j=1;j<=M;j++)

       {

           hm[cur^1].init();

           if(maze[i][j])dpblank(i,j,cur);

           else dpblock(i,j,cur);

           cur^=1;

       }

    for(i=0;i<hm[cur].size;i++)

      if(hm[cur].dp[i]>ans)

        ans=hm[cur].dp[i];

    printf("%d\n",ans);

}

int main()

{

  //  freopen("in.txt","r",stdin);

  //  freopen("out.txt","w",stdout);

    int T;

    scanf("%d",&T);

    while(T--)

    {

        init();

        solve();

    }

    return 0;

}







/*

Sample Input



2

1 1

10

1 2

5 0



Sample Output



10

5





*/

 

 

 

 

 

HDU 4285  circuits   这是天津网络赛的题目。求K个回路的方案数。而且不能是环套环。

增加个标志位来记录形成的回路个数。而且注意避免环套环的情况。

解题报告

http://www.cnblogs.com/kuangbin/archive/2012/10/02/2710308.html

 

HDU 4285
/*

HDU 4285

要形成刚好K条回路的方法数

要避免环套环的情况。

所以形成回路时,要保证两边的插头数是偶数



G++ 11265ms  11820K

C++ 10656ms  11764K



*/



#include<stdio.h>

#include<iostream>

#include<algorithm>

#include<string.h>

using namespace std;



const int MAXD=15;

const int STATE=1000010;

const int HASH=300007;//这个大一点可以防止TLE,但是容易MLE

const int MOD=1000000007;



int N,M,K;

int maze[MAXD][MAXD];

int code[MAXD];

int ch[MAXD];

int num;//圈的个数

struct HASHMAP

{

    int head[HASH],next[STATE],size;

    long long state[STATE];

    int f[STATE];

    void init()

    {

        size=0;

        memset(head,-1,sizeof(head));

    }

    void push(long long st,int ans)

    {

        int i;

        int h=st%HASH;

        for(i=head[h];i!=-1;i=next[i])

          if(state[i]==st)

          {

              f[i]+=ans;

              f[i]%=MOD;

              return;

          }

        state[size]=st;

        f[size]=ans;

        next[size]=head[h];

        head[h]=size++;

    }

}hm[2];

void decode(int *code,int m,long long  st)

{

    num=st&63;

    st>>=6;

    for(int i=m;i>=0;i--)

    {

        code[i]=st&7;

        st>>=3;

    }

}

long long encode(int *code,int m)//最小表示法

{

    int cnt=1;

    memset(ch,-1,sizeof(ch));

    ch[0]=0;

    long long st=0;

    for(int i=0;i<=m;i++)

    {

        if(ch[code[i]]==-1)ch[code[i]]=cnt++;

        code[i]=ch[code[i]];

        st<<=3;

        st|=code[i];

    }

    st<<=6;

    st|=num;

    return st;

}

void shift(int *code,int m)

{

    for(int i=m;i>0;i--)code[i]=code[i-1];

    code[0]=0;

}

void dpblank(int i,int j,int cur)

{

    int k,left,up;

    for(k=0;k<hm[cur].size;k++)

    {

        decode(code,M,hm[cur].state[k]);

        left=code[j-1];

        up=code[j];

        if(left&&up)

        {

            if(left==up)

            {

                if(num>=K)continue;

                int t=0;

                //要避免环套环的情况,需要两边插头数为偶数

                for(int p=0;p<j-1;p++)

                  if(code[p])t++;

                if(t&1)continue;

                if(num<K)

                {

                    num++;

                    code[j-1]=code[j]=0;

                    hm[cur^1].push(encode(code,j==M?M-1:M),hm[cur].f[k]);

                }

            }

            else

            {

                code[j-1]=code[j]=0;

                for(int t=0;t<=M;t++)

                  if(code[t]==up)

                    code[t]=left;

                hm[cur^1].push(encode(code,j==M?M-1:M),hm[cur].f[k]);

            }

        }

        else if(left||up)

        {

            int t;

            if(left)t=left;

            else t=up;

            if(maze[i][j+1])

            {

                code[j-1]=0;

                code[j]=t;

                hm[cur^1].push(encode(code,M),hm[cur].f[k]);

            }

            if(maze[i+1][j])

            {

                code[j]=0;

                code[j-1]=t;

                hm[cur^1].push(encode(code,j==M?M-1:M),hm[cur].f[k]);

            }

        }

        else

        {

            if(maze[i][j+1]&&maze[i+1][j])

            {

                code[j-1]=code[j]=13;

                hm[cur^1].push(encode(code,j==M?M-1:M),hm[cur].f[k]);

            }

        }

    }

}

void dpblock(int i,int j,int cur)

{

    int k;

    for(k=0;k<hm[cur].size;k++)

    {

        decode(code,M,hm[cur].state[k]);

        code[j-1]=code[j]=0;

        hm[cur^1].push(encode(code,j==M?M-1:M),hm[cur].f[k]);

    }

}

char str[20];

void init()

{

    scanf("%d%d%d",&N,&M,&K);

    memset(maze,0,sizeof(maze));

    for(int i=1;i<=N;i++)

    {

        scanf("%s",&str);

        for(int j=1;j<=M;j++)

          if(str[j-1]=='.')

            maze[i][j]=1;

    }

}

void solve()

{

    int i,j,cur=0;

    hm[cur].init();

    hm[cur].push(0,1);

    for(i=1;i<=N;i++)

      for(j=1;j<=M;j++)

      {

          hm[cur^1].init();

          if(maze[i][j])dpblank(i,j,cur);

          else dpblock(i,j,cur);

          cur^=1;

      }

    int ans=0;

    for(i=0;i<hm[cur].size;i++)

      if(hm[cur].state[i]==K)

      {

          ans+=hm[cur].f[i];

          ans%=MOD;

      }

    printf("%d\n",ans);



}

int main()

{

    //freopen("in.txt","r",stdin);

    //freopen("out.txt","w",stdout);

    int T;

    scanf("%d",&T);

    while(T--)

    {

        init();

        solve();

    }

    return 0;

}



/*

Sample Input

2

4 4 1

**..

....

....

....

4 4 1

....

....

....

....





Sample Output

2

6



*/
HDU4285
/*

HDU  4285

要形成刚好K条回路的方法数

要避免环套环的情况。

所以形成回路时,要保证两边的插头数是偶数

G++ 11765ms  12560K

C++  11656ms 12504K

*/



#include<stdio.h>

#include<iostream>

#include<algorithm>

#include<string.h>

using namespace std;



const int MAXD=15;

const int STATE=1000010;

const int HASH=100007;

const int MOD=1000000007;



int N,M,K;

int maze[MAXD][MAXD];

int code[MAXD];

int ch[MAXD];



struct HASHMAP

{

    int head[HASH],next[STATE],size;

    long long state[STATE];

    int f[STATE];

    int cir[STATE];//形成的圈的个数

    void init()

    {

        size=0;

        memset(head,-1,sizeof(head));

    }

    void push(long long st,int ans,int _cir)

    {

        int i,h=st%HASH;

        for(i=head[h];i!=-1;i=next[i])

          if(state[i]==st&&cir[i]==_cir)

          {

              f[i]+=ans;

              f[i]%=MOD;

              return;

          }

        state[size]=st;

        f[size]=ans;

        cir[size]=_cir;

        next[size]=head[h];

        head[h]=size++;

    }

}hm[2];

void decode(int *code,int m,long long  st)

{

    for(int i=m;i>=0;i--)

    {

        code[i]=st&7;

        st>>=3;

    }

}

long long encode(int *code,int m)//最小表示法

{

    int cnt=1;

    memset(ch,-1,sizeof(ch));

    ch[0]=0;

    long long st=0;

    for(int i=0;i<=m;i++)

    {

        if(ch[code[i]]==-1)ch[code[i]]=cnt++;

        code[i]=ch[code[i]];

        st<<=3;

        st|=code[i];

    }

    return st;

}

void shift(int *code,int m)

{

    for(int i=m;i>0;i--)code[i]=code[i-1];

    code[0]=0;

}

void dpblank(int i,int j,int cur)

{

    int k,left,up;

    for(k=0;k<hm[cur].size;k++)

    {

        decode(code,M,hm[cur].state[k]);

        left=code[j-1];

        up=code[j];

        if(left&&up)

        {

            if(left==up)

            {

                if(hm[cur].cir[k]>=K)continue;

                int t=0;

                for(int p=0;p<j-1;p++)

                  if(code[p])t++;

                if(t&1)continue;

                if(hm[cur].cir[k]<K)

                {

                    code[j-1]=code[j]=0;

                    hm[cur^1].push(encode(code,j==M?M-1:M),hm[cur].f[k],hm[cur].cir[k]+1);

                }

            }

            else

            {

                code[j-1]=code[j]=0;

                for(int t=0;t<=M;t++)

                  if(code[t]==up)

                    code[t]=left;

                hm[cur^1].push(encode(code,j==M?M-1:M),hm[cur].f[k],hm[cur].cir[k]);

            }

        }

        else if(left||up)

        {

            int t;

            if(left)t=left;

            else t=up;

            if(maze[i][j+1])

            {

                code[j-1]=0;

                code[j]=t;

                hm[cur^1].push(encode(code,M),hm[cur].f[k],hm[cur].cir[k]);

            }

            if(maze[i+1][j])

            {

                code[j]=0;

                code[j-1]=t;

                hm[cur^1].push(encode(code,j==M?M-1:M),hm[cur].f[k],hm[cur].cir[k]);

            }

        }

        else

        {

            if(maze[i][j+1]&&maze[i+1][j])

            {

                code[j-1]=code[j]=13;

                hm[cur^1].push(encode(code,j==M?M-1:M),hm[cur].f[k],hm[cur].cir[k]);

            }

        }

    }

}

void dpblock(int i,int j,int cur)

{

    int k;

    for(k=0;k<hm[cur].size;k++)

    {

        decode(code,M,hm[cur].state[k]);

        code[j-1]=code[j]=0;

        hm[cur^1].push(encode(code,j==M?M-1:M),hm[cur].f[k],hm[cur].cir[k]);

    }

}

char str[20];

void init()

{

    scanf("%d%d%d",&N,&M,&K);

    memset(maze,0,sizeof(maze));

    for(int i=1;i<=N;i++)

    {

        scanf("%s",&str);

        for(int j=1;j<=M;j++)

          if(str[j-1]=='.')

            maze[i][j]=1;

    }

}

void solve()

{

    int i,j,cur=0;

    hm[cur].init();

    hm[cur].push(0,1,0);

    for(i=1;i<=N;i++)

      for(j=1;j<=M;j++)

      {

          hm[cur^1].init();

          if(maze[i][j])dpblank(i,j,cur);

          else dpblock(i,j,cur);

          cur^=1;

      }

    int ans=0;

    for(i=0;i<hm[cur].size;i++)

      if(hm[cur].cir[i]==K)

      {

          ans+=hm[cur].f[i];

          ans%=MOD;

      }

    printf("%d\n",ans);



}

int main()

{

  //  freopen("in.txt","r",stdin);

 //   freopen("out.txt","w",stdout);

    int T;

    scanf("%d",&T);

    while(T--)

    {

        init();

        solve();

    }

    return 0;

}



/*

Sample Input

2

4 4 1

**..

....

....

....

4 4 1

....

....

....

....





Sample Output

2

6



*/

 

 

 

上面各题的代码,我都是用一样的模板写的,写的风格都是一致的。插头DP写起来比较难。而且容易写错。更加复杂的插头DP真的很难想到。

但是插头DP还是很美妙的。更多插头DP的题目等我做了会更新上去的。

持续更新中......

2012-10-2   by kuangbin

你可能感兴趣的:(ACM)