【POJ】3133 Manhattan Wiring

【算法】插头DP

【题解】蓝书原题 

动态规划

[原创]插头DP小结(ACM by kuangbin)

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int MAXD=15;
const int MOD=10007;
const int STATE=500010;
int n,m;
int map[MAXD][MAXD];
int code[MAXD];
struct HASHMAP{
    int first[MOD],tot,from[STATE],state[STATE],f[STATE];
    void init()//清空 
    {
        tot=0;
        memset(first,0,sizeof(first));
    }
    void push(int number,int num)//插入 
    {
        int x=number%MOD;
        for(int i=first[x];i;i=from[i])
         if(number==state[i])
          {
              f[i]=min(f[i],num);
              return;//已有,不用加入,直接return 
          }
        tot++;
        state[tot]=number;
        f[tot]=num;
        from[tot]=first[x];
        first[x]=tot;
    }
}hm[2];

void decode(int st)//解码
{
    for(int i=m;i>=0;i--)//数组第m位是最右边 
     {
         code[i]=(st&3);//'&'取出1 
         st>>=2;
     }
}
int encode()//四进制,0无插头 2、3插头 
{
    int st=0;
    for(int i=0;i<=m;i++)
     {
         st<<=2;
         st|=code[i];
     }
    return st;
}
void shift()//指向右边界的插头不存在,空出了一位,全体右移一位,把最左插头空出来 
{
    for(int i=m;i>0;i--)code[i]=code[i-1];
    code[0]=0;
}
void dpblank(int i,int j,int cur)//空格map[i][j]=1 
{
    int left,up;
    for(int k=1;k<=hm[cur].tot;k++)
     {
         decode(hm[cur].state[k]);
         left=code[j-1];//左在本体,本来是j,但因为从0开始编号…… 
         up=code[j];
         if(left&&up)
          {
              if(left==up)//只接受相同插头 
               {
                   code[j-1]=code[j]=0;//设置 
                   if(j==m)shift();//过行 
                   hm[cur^1].push(encode(),hm[cur].f[k]+1);//入队 
               }
          }
         else if((left&&(!up))||(!left)&&up)
          {
              int t;
              if(left)t=left;else t=up;//从左或从上 
              if(map[i][j+1]==1||map[i][j+1]==t)//往右
              {
                  code[j-1]=0;code[j]=t;
                  hm[cur^1].push(encode(),hm[cur].f[k]+1);
              }
            if(map[i+1][j]==1||map[i+1][j]==t)//往下
             {
                 code[j-1]=t;code[j]=0;
                 if(j==m)shift(); 
                 hm[cur^1].push(encode(),hm[cur].f[k]+1);
             } 
          }
         else
          {
              code[j-1]=code[j]=0;//留空 
              if(j==m)shift();
              hm[cur^1].push(encode(),hm[cur].f[k]);
              if(map[i][j+1]&&map[i+1][j])//开左下插头 
               {
                   int r=map[i][j+1],d=map[i+1][j];
                   if(r==1&&(d==1||d==2)||r==2&&(d==1||d==2))
                    {
                        decode(hm[cur].state[k]);//刚可能换行破坏了,重新解码(挺快的) 
                        code[j-1]=code[j]=2;
                        hm[cur^1].push(encode(),hm[cur].f[k]+1);
                    }
                   if(r==1&&(d==1||d==3)||r==3&&(d==1||d==3))
                    {
                        decode(hm[cur].state[k]);
                        code[j-1]=code[j]=3;
                        hm[cur^1].push(encode(),hm[cur].f[k]+1);
                    }
               }
          }
     }
}
void dpblock(int i,int j,int cur)
{
    for(int k=1;k<=hm[cur].tot;k++)
     {
         decode(hm[cur].state[k]);
        code[j-1]=code[j]=0;
        if(j==m)shift();
        hm[cur^1].push(encode(),hm[cur].f[k]);
     }
}
void dp_begin(int i,int j,int cur,int x)//只接受一个插头(凭空消失)或只从一边出去(凭空出现) 
{
    int left,up;
    for(int k=1;k<=hm[cur].tot;k++)
     {
         decode(hm[cur].state[k]);
         left=code[j-1];up=code[j];
         if(left+up==x)
          {
              code[j-1]=code[j]=0;
              if(j==m)shift();
              hm[cur^1].push(encode(),hm[cur].f[k]+1);//半条边最后再减掉,避免浮点数运算 
          }
         if(left+up==0)
          {
              if(map[i][j+1]==1||map[i][j+1]==x)
               {
                   code[j-1]=0;code[j]=x;
                   hm[cur^1].push(encode(),hm[cur].f[k]+1);
               }
              if(map[i+1][j]==1||map[i+1][j]==x)
               {
                   code[j-1]=x;code[j]=0;
                   if(j==m)shift();
                   hm[cur^1].push(encode(),hm[cur].f[k]+1);
               }
          }
     }
}
int main()
{
    scanf("%d%d",&n,&m);
    while(n!=0||m!=0)
     {
         memset(map,0,sizeof(map));
         for(int i=1;i<=n;i++)
          for(int j=1;j<=m;j++)
           {
               int u;
               scanf("%d",&u);
               if(u==0)map[i][j]=1;
               if(u==1)map[i][j]=0;
               if(u>1)map[i][j]=u;
           }
        int cur=0;hm[0].init();hm[0].push(0,0);
        for(int i=1;i<=n;i++)
         for(int j=1;j<=m;j++)
          {
              hm[cur^1].init();//根据上一格状态板刷这一格
            if(map[i][j]==0)dpblock(i,j,cur);
             else if(map[i][j]==1)dpblank(i,j,cur);
             else if(map[i][j]==2)dp_begin(i,j,cur,2);
             else if(map[i][j]==3)dp_begin(i,j,cur,3);
            cur^=1; 
          }
        int ans=0;
        for(int i=1;i<=hm[cur].tot;i++)
         ans+=hm[cur].f[i];
        if(ans>0)ans-=2;
        printf("%d\n",ans);
         scanf("%d%d",&n,&m);
     }
}
View Code

 

posted @ 2017-04-21 19:30  ONION_CYC  阅读(240)  评论(0编辑  收藏  举报