[原创]插头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

posted on 2012-10-02 15:10  kuangbin  阅读(19814)  评论(1编辑  收藏  举报

导航

JAVASCRIPT: