[原創]插頭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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM