POJ 3133 Manhattan Wiring(插头DP)

Manhattan Wiring
Time Limit: 5000MS   Memory Limit: 65536K
Total Submissions: 1112   Accepted: 636

Description

There is a rectangular area containing n × m cells. Two cells are marked with “2”, and another two with “3”. Some cells are occupied by obstacles. You should connect the two “2”s and also the two “3”s with non-intersecting lines. Lines can run only vertically or horizontally connecting centers of cells without obstacles.

Lines cannot run on a cell with an obstacle. Only one line can run on a cell at most once. Hence, a line cannot intersect with the other line, nor with itself. Under these constraints, the total length of the two lines should be minimized. The length of a line is defined as the number of cell borders it passes. In particular, a line connecting cells sharing their border has length 1.

Fig. 1(a) shows an example setting. Fig. 1(b) shows two lines satisfying the constraints above with minimum total length 18.

POJ 3133 Manhattan Wiring(插头DP)

Figure 1: An example of setting and its solution

Input

The input consists of multiple datasets, each in the following format.

n m
row1
rown

n is the number of rows which satisfies 2 ≤ n ≤ 9. m is the number of columns which satisfies 2 ≤ m ≤ 9. Each rowi is a sequence of m digits separated by a space. The digits mean the following.

0: Empty

1: Occupied by an obstacle

2: Marked with “2”

3: Marked with “3”

The end of the input is indicated with a line containing two zeros separated by a space.

Output

For each dataset, one line containing the minimum total length of the two lines should be output. If there is no pair of lines satisfying the requirement, answer “0” instead. No other characters should be contained in the output.

Sample Input

5 5

0 0 0 0 0

0 0 0 3 0

2 0 2 0 0

1 0 1 1 1

0 0 0 0 3

2 3

2 2 0

0 3 3

6 5

2 0 0 0 0

0 3 0 0 0

0 0 0 0 0

1 1 1 0 0

0 0 0 0 0

0 0 2 3 0

5 9

0 0 0 0 0 0 0 0 0

0 0 0 0 3 0 0 0 0

0 2 0 0 0 0 0 2 0

0 0 0 0 3 0 0 0 0

0 0 0 0 0 0 0 0 0

9 9

3 0 0 0 0 0 0 0 2

0 0 0 0 0 0 0 0 0

0 0 0 0 0 0 0 0 0

0 0 0 0 0 0 0 0 0

0 0 0 0 0 0 0 0 0

0 0 0 0 0 0 0 0 0

0 0 0 0 0 0 0 0 0

0 0 0 0 0 0 0 0 0

2 0 0 0 0 0 0 0 3

9 9

0 0 0 1 0 0 0 0 0

0 2 0 1 0 0 0 0 3

0 0 0 1 0 0 0 0 2

0 0 0 1 0 0 0 0 3

0 0 0 1 1 1 0 0 0

0 0 0 0 0 0 0 0 0

0 0 0 0 0 0 0 0 0

0 0 0 0 0 0 0 0 0

0 0 0 0 0 0 0 0 0

9 9

0 0 0 0 0 0 0 0 0

0 3 0 0 0 0 0 0 0

0 0 0 0 0 0 0 0 0

0 0 0 0 0 0 0 0 0

0 0 0 0 0 0 0 0 0

0 0 0 0 0 0 0 0 0

0 0 0 0 0 0 0 0 0

0 0 0 0 0 0 0 0 0

0 0 0 0 0 0 2 3 2

0 0

Sample Output

18

2

17

12

0

52

43

Source

 
 
插头DP,上次做这题的时候写了篇报告:http://www.cnblogs.com/kuangbin/archive/2012/09/28/2707416.html
熟悉插头DP后,按照自己的模板风格又写了一遍。
查了好久的错误,插头DP非常容易写错。
/*

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;

}

 

你可能感兴趣的:(poj)