POJ 3133 Manhattan Wiring (插头DP,轮廓线,经典)

 

 

题意:给一个n*m的矩阵,每个格子中有1个数,可能是0或2或3,出现2的格子数为2个,出现3的格子数为2个,要求将两个2相连,两个3相连,求不交叉的最短路(起终点只算0.5长,其他算1)。

 

思路:

  这题与普通插头DP有些区别了,就是要求最短路时,而且还要同时两条不相交的最短路。一开始看还是感觉挺难的,因为每个0格子还得考虑两种线,分别是2线和3线,而且还不能出现回路的,于是就想用之前的模板,括号表示法,再加上1个位来表示2/3线,即共3个位来表示一个插头信息,但是写了才发现,括号表示不了,比如仅让右括号进2/3的格子时,如果2/3的格子在最左边时就不行了,同理左括号也不行,并不是真不行,是有点难搞。

 

  于是盯着刘汝佳的书看,仅需3进制就能实现,这么神奇?只需要标记是2/3线和无线,这3种而已,这里有个问题是“如果产生回路了,怎么办”?由于我们求得是最短路,如果有一个状态是出现回路的,那么这个回路肯定是不必要的,由于在g[i][j]==0时,还可以跳过这些格子,所以肯定也有一个状态也是一样的,只是没有了回路,而且路程更短。总之,如果出现回路,在取最小值时,会自动被代替掉了,因为同样的状态总是会有更短的。

 

 

  1 //#include <bits/stdc++.h>
  2 #include <iostream>
  3 #include <cstdio>
  4 #include <cstring>
  5 #define pii pair<int,int>
  6 #define INF 0x3f3f3f3f
  7 #define LL long long
  8 using namespace std;
  9 const int N=20;
 10 LL ans;
 11 char g[N][N];
 12 int cur, n, m, ex, ey;
 13 struct Hash_Map
 14 {
 15     static const int mod=12357;
 16     static const int NN=20010;
 17     int head[mod];      //桶指针
 18     int next[NN];        //记录链的信息
 19     LL  status[NN];      //状态
 20     LL  value[NN];       //状态对应的DP值。
 21     int size;
 22 
 23     void clear()    //清除哈希表中的状态
 24     {
 25         memset(head, -1, sizeof(head));
 26         size = 0;
 27     }
 28 
 29     void insert(LL st, LL val)  //插入状态st的值为val
 30     {
 31         int h = st%mod;
 32         for(int i=head[h]; i!=-1; i=next[i])
 33             if(status[i] == st) //这个状态已经存在,累加进去。
 34             {
 35                 value[i] =min(value[i], val);
 36                 return ;
 37             }
 38         status[size]= st;           //找不到状态st,则插入st。
 39         value[size] = val;
 40         next[size] = head[h] ;      //新插入的元素在队头
 41         head[h] = size++;
 42     }
 43 }hashmap[2];
 44 
 45 inline int getbit(LL s,int pos)   //取出状态s的第pos个插头
 46 {
 47     return (s>>2*pos)&3;
 48 }
 49 inline int setbit(LL s,int pos,int bit)   //将状态s的第pos个插头设置为bit
 50 {
 51     if(s&(1<<2*pos ))     s^=1<<(2*pos);
 52     if(s&(1<<(2*pos+1)))  s^=1<<(2*pos+1);
 53     return (s|(bit<<2*pos));
 54 }
 55 
 56 void DP(int i,int j)
 57 {
 58     for(int k=0; k<hashmap[cur^1].size; k++)
 59     {
 60         LL s=hashmap[cur^1].status[k];
 61         LL v=hashmap[cur^1].value[k];
 62         LL t=(setbit(s,j,0)&setbit(s,j+1,0));
 63         int R=getbit(s,j), D=getbit(s,j+1);
 64 
 65         if(g[i][j]==1)    //障碍格子
 66         {
 67             if( R==0 && D==0 )    hashmap[cur].insert(s, v);
 68             continue ;
 69         }
 70         if(R&&D)    //相同就能合并,具体看思路
 71         {
 72             if( g[i][j]==0 &&  R==D )      hashmap[cur].insert(t,v+1);
 73         }
 74         else if(R||D)   //单括号:要么需要延续,要么是到达2或3
 75         {
 76             int big=R+D;
 77             if(g[i][j]==2 && big==1 )    hashmap[cur].insert(t, v+1);   //到达
 78             if(g[i][j]==3 && big==2 )    hashmap[cur].insert(t, v+1);
 79             if(g[i][j]==0 && i+1<n )     hashmap[cur].insert(setbit(t,j,big),  v+1);    //延续
 80             if(g[i][j]==0 && j+1<m )     hashmap[cur].insert(setbit(t,j+1,big),v+1);
 81         }
 82         else    //无括号
 83         {
 84             if(g[i][j]==2)
 85             {
 86                 if(i+1<n)   hashmap[cur].insert( setbit(s,j,1),   v+1 );
 87                 if(j+1<m)   hashmap[cur].insert( setbit(s,j+1,1), v+1 );
 88             }
 89             if(g[i][j]==3)
 90             {
 91                 if(i+1<n)   hashmap[cur].insert( setbit(s,j,2),   v+1 );
 92                 if(j+1<m)   hashmap[cur].insert( setbit(s,j+1,2), v+1 );
 93             }
 94             if(g[i][j]==0)
 95             {
 96                 hashmap[cur].insert(s,v);   //跳过此格子
 97                 if(i+1<n && j+1<m)    hashmap[cur].insert(setbit(t,j,1)|setbit(t,j+1,1), v+1 );
 98                 if(i+1<n && j+1<m)    hashmap[cur].insert(setbit(t,j,2)|setbit(t,j+1,2), v+1 );
 99 
100             }
101         }
102 
103     }
104 
105 }
106 
107 void cal()
108 {
109     for(int i=0; i<n; i++)
110     {
111         cur^=1;
112         hashmap[cur].clear();
113         for(int j=0; j<hashmap[cur^1].size; j++)    //新行,需要左移一下状态。
114             hashmap[cur].insert( hashmap[cur^1].status[j]<<2, hashmap[cur^1].value[j] );
115         for(int j=0; j<m; j++)
116         {
117             cur^=1;
118             hashmap[cur].clear();
119             DP(i,j);
120         }
121     }
122 }
123 
124 LL print()
125 {
126     for(int i=0; i<hashmap[cur].size; i++)
127         if(hashmap[cur].status[i]==0)
128             return hashmap[cur].value[i];
129     return 0;
130 }
131 
132 int main()
133 {
134     //freopen("input.txt", "r", stdin);
135     while(scanf("%d%d",&n,&m), n+m)
136     {
137         for(int i=0; i<n; i++)
138             for(int j=0; j<m; j++)
139                 scanf("%d",&g[i][j]);
140 
141         cur=0;
142         hashmap[cur].clear();
143         hashmap[cur].insert(0, 0);
144         cal();
145         ans=print();
146         cout<<(ans==0?0:ans-2)<<endl;
147     }
148     return 0;
149 }
AC代码

 

 

 

  自己的想法是这样的:用括号表示法来表示插头,由于有两类线,所以增加一个位来区分2/3线,共3个位表示一个插头信息,所以用1和2表示2线的括号,5和6表示3线的括号,0表示无括号。当遇到2/3格子时,要么只接受单个插头进来,要么只有单方向出去,而且出去的可以是左/右括号,这样才能保证连起来;遇到0空格时,可以不走,也可以产生一对括号,分别是(1,2)和(5,6)这两种。其他的基本一样,实践证明还是可以得,只不过时间复杂度稍微就高了,因为多了很多无用的状态。而且有个注意点是,当同向括号'(('出现时,仍然要合并,而且要改另一个右括号为(,但是这'(('中可能有1个是来自2/3格子的,它仅是单向的线,所以有可能是找不到另一半的,如果找不到另一半就忽略,找到了就改(这说明不是来自2/3格子)。

  注:本代码在POJ上是TLE,但是在UVA或UVALive都是可以过的。

 

  1 //#include <bits/stdc++.h>
  2 #include <iostream>
  3 #include <cstdio>
  4 #include <cstring>
  5 #define pii pair<int,int>
  6 #define INF 0x3f3f3f3f
  7 #define LL long long
  8 using namespace std;
  9 const int N=15;
 10 int n, m, cur, g[N][N];
 11 struct Hash_Map
 12 {
 13     static const int mod=12357;
 14     static const int NN=200100;
 15     int head[mod];       //桶指针
 16     int next[NN];        //记录链的信息
 17     LL  status[NN];      //状态
 18     LL  value[NN];       //状态对应的DP值。
 19     int size;
 20 
 21     void clear()    //清除哈希表中的状态
 22     {
 23         memset(head, -1, sizeof(head));
 24         size = 0;
 25     }
 26 
 27     void insert(LL st, LL val)  //插入状态st的值为val
 28     {
 29         int h = st%mod;
 30         for(int i=head[h]; i!=-1; i=next[i])
 31         {
 32             if(status[i] == st) //这个状态已经存在,累加进去。
 33             {
 34                 value[i] = min(value[i], val);
 35                 return ;
 36             }
 37         }
 38 
 39         status[size]= st;           //找不到状态st,则插入st。
 40         value[size] = val;
 41         next[size] = head[h] ;      //新插入的元素在队头
 42         head[h] = size++;
 43     }
 44 }hashmap[2];
 45 
 46 LL setbit(LL s,int pos,int bit)   //将状态s的第pos个插头设置为bit
 47 {
 48     //if(s&(1<<3*pos ))     s^=(LL)1<<(3*pos);
 49     //if(s&(1<<(3*pos+1)))  s^=(LL)1<<(3*pos+1);
 50     //if(s&(1<<(3*pos+2)))  s^=(LL)1<<(3*pos+2);
 51     s&=~(7<<3*pos);
 52     return (s|((LL)bit<<3*pos));
 53 }
 54 
 55 int getbit(LL s,int pos)
 56 {
 57     return ((s>>3*pos)&7);
 58 }
 59 
 60 LL Fr(LL s,int pos,int bit,int rep)   //寻找状态s的第pos个插头对应的右括号。
 61 {
 62     int cnt=0;
 63     for(pos+=2; pos<m; pos++)
 64     {
 65         if(getbit(s, pos)==rep)   cnt++;
 66         if(getbit(s, pos)==bit)   cnt--;
 67         if(cnt==-1)  return setbit(s, pos, rep);
 68     }
 69     return s;
 70 }
 71 LL Fl(LL s,int pos,int bit,int rep)   //寻找状态s的第pos个插头对应的左括号。
 72 {
 73     int cnt=0;
 74     for(pos--; pos>=0; pos--)
 75     {
 76         if(getbit(s, pos)==rep)   cnt++;
 77         if(getbit(s, pos)==bit)        cnt--;
 78         if( cnt==-1)    return setbit(s, pos, rep);
 79     }
 80     return s;   //注意点,有可能找不到对应的另一半括号,Fr同理。
 81 }
 82 void DP(int i,int j)
 83 {
 84     for(int k=0; k<hashmap[cur^1].size; k++)
 85     {
 86         LL s=hashmap[cur^1].status[k];
 87         LL v=hashmap[cur^1].value[k];
 88         LL t=(setbit(s,j,0)&setbit(s,j+1,0));
 89         int R=getbit(s, j), D=getbit(s, j+1);
 90         if( g[i][j]==1 )    //障碍
 91         {
 92             if(R==0 && D==0)    hashmap[cur].insert(s,v);
 93             continue;
 94         }
 95         if(R&&D)
 96         {
 97             if( g[i][j]==2 || g[i][j]==3 || (R&4)!=(D&4) )  continue;   //只能容许1条线进来
 98             if( R==D )
 99             {
100                 if(R==1)      t=Fr(t,j,2,1);  //
101                 if(R==2)      t=Fl(t,j,1,2);  //
102                 if(R==5)      t=Fr(t,j,6,5);
103                 if(R==6)      t=Fl(t,j,5,6);
104                 hashmap[cur].insert(t, v-1); //当前块计算了2次
105             }
106             else if( R==2 && D==1 )   hashmap[cur].insert(t,v-1);
107             else if( R==6 && D==5 )   hashmap[cur].insert(t,v-1);
108         }
109         else if(R||D)   //单括号
110         {
111             R=max(R,D);
112             if( g[i][j]==2 && (R==1||R==2) )      hashmap[cur].insert(t, v);
113             if( g[i][j]==3 && (R==5||R==6) )      hashmap[cur].insert(t, v);
114             if( g[i][j]==0 && j+1<m )    hashmap[cur].insert(setbit(t,j+1,R), v+1); //延续
115             if( g[i][j]==0 && i+1<n )    hashmap[cur].insert(setbit(t,j,R), v+1);
116         }
117         else    //无括号
118         {
119             if(g[i][j]==2)  //起终点格子:只可以单线出。
120             {
121                 if(i+1<n) hashmap[cur].insert(setbit(t,j,1) ,  v+2);     //
122                 if(i+1<n) hashmap[cur].insert(setbit(t,j,2) ,  v+2);     //
123                 if(j+1<m) hashmap[cur].insert(setbit(t,j+1,1), v+2);     //
124                 if(j+1<m) hashmap[cur].insert(setbit(t,j+1,2), v+2);     //
125             }
126             if(g[i][j]==3)
127             {
128                 if(i+1<n) hashmap[cur].insert(setbit(t,j,5) ,  v+2);   //
129                 if(i+1<n) hashmap[cur].insert(setbit(t,j,6) ,  v+2);   //
130                 if(j+1<m) hashmap[cur].insert(setbit(t,j+1,5), v+2);   //
131                 if(j+1<m) hashmap[cur].insert(setbit(t,j+1,6), v+2);   //
132             }
133             if(g[i][j]==0)
134             {
135                 hashmap[cur].insert(s, v);  //本格子不走
136                 if(i+1<n && j+1<m)
137                 {
138                     hashmap[cur].insert(setbit(t,j,1)|setbit(t,j+1,2), v+3); //2线角头
139                     hashmap[cur].insert(setbit(t,j,5)|setbit(t,j+1,6), v+3); //3线角头
140                 }
141             }
142         }
143     }
144 }
145 
146 
147 
148 void cal()
149 {
150     for(int i=0; i<n; i++)
151     {
152         cur^=1;
153         hashmap[cur].clear();
154         for(int j=0; j<hashmap[cur^1].size; j++)    //新行,需要左移一下状态。
155             hashmap[cur].insert( hashmap[cur^1].status[j]<<3, hashmap[cur^1].value[j] );
156         for(int j=0; j<m; j++)
157         {
158             cur^=1;
159             hashmap[cur].clear();
160             DP(i,j);
161         }
162     }
163 }
164 LL print()
165 {
166     for(int i=0; i<hashmap[cur].size; i++)
167         if(hashmap[cur].status[i]==0)
168             return hashmap[cur].value[i];
169     return 0;
170 }
171 
172 int main()
173 {
174     freopen("input.txt", "r", stdin);
175     while(scanf("%d%d",&n,&m), n+m)
176     {
177         for(int i=0; i<n; i++)
178             for(int j=0; j<m; j++)
179                 scanf("%d",&g[i][j]);
180 
181         cur=0;
182         hashmap[cur].clear();
183         hashmap[cur].insert(0, 0);
184         cal();
185         LL ans=print();
186         printf("%lld\n",ans==0?0:ans-2);
187     }
188     return 0;
189 }
AC代码

 

转载于:https://www.cnblogs.com/xcw0754/p/4795983.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值