【POJ3133】Manhattan Wiring (插头dp)
Manhattan Wiring
题意:
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.
大意:将2,2连接,3,3连接,两条路径不相交求长度最小值
思路:
因为只用求序列的最小值,所以也不需要记录括号序列,只用区分是连接\(2\)还是连接\(3\)
在\(dp\)中存四进制数,有1表示连2的插头,有2表示连3的插头,然后分开讨论
\(b_1\)表示左插头的值,\(b_2\)表示上插头的值
点为1
只能转移空格子
点为 2/3
1、只有左插头或者上插头,标号相同时可以转移,代表是终点
2、既没有左插头也没有右插头,加一个向右或者向下的插头,编号就是这个格子的值
3、既有左又有上不合法
点为0
1、如果\(!(b1|b2)\)
①不加
②加一个右和下插头,标号为2或3
2、\(b_1\)&\(b_2\)
①如果标号相同,直接连上
②不相同,不合法
3、\(b_1|b_2\)
如果是左插头,多加向右或者向下的
如果是上插头,同理
最后的答案用最后一个点
//POJ 3133
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
bool cur1;
void Rd(int &res) {
res=0;
char c;
while(c=getchar(),c<48);
do res=(res<<1)+(res<<3)+(c^48);
while(c=getchar(),c>=48);
}
#define M 300005
const int Mod=299987;
int n,m,a[15][15],op,cnt[2],ex,ey,pr[1<<20],la[M],to[2][1<<20],inv[15],ans=0,dp[2][M];
void Min(int &x,int y) {
if(x==-1||x>y)x=y;
}
void add(int bit,int v) {
int u=(bit%Mod)+1;
for(int i=la[u]; i; i=pr[i])
if(to[op][i]==bit) {
Min(dp[op][i],v);
return;
}
to[op][++cnt[op]]=bit,pr[cnt[op]]=la[u],la[u]=cnt[op],dp[op][cnt[op]]=v;
}
bool cur2;
void Print(int x) {
for(int i=0; i<=m; i++)printf("%d",x%4),x>>=2;
}
int main() {
inv[0]=1;
for(int i=1; i<=10; i++)inv[i]=(inv[i-1]<<2);
while(1) {
Rd(n),Rd(m);
if(!(n|m))break;
int ex,ey;
for(int i=1; i<=n; i++)for(int j=1; j<=m; j++)Rd(a[i][j]);
for(int i=1;i<=n;i++)a[i][m+1]=1;
for(int j=1;j<=m;j++)a[n+1][j]=1;
memset(dp,63,sizeof(dp));
memset(la,0,sizeof(la));
int res=inv[m+1]-1;
op=0,cnt[0]=1,to[0][1]=0,dp[0][1]=0;
for(int i=1; i<=n; i++) {
for(int k=1; k<=cnt[op]; k++)to[op][k]<<=2,to[op][k]&=res;
for(int j=1; j<=m; j++) {
op^=1,cnt[op]=0,memset(la,0,sizeof(la));
for(int k=1; k<=cnt[op^1]; k++) {
int v=dp[op^1][k],od=to[op^1][k],b1=((od>>(2*(j-1)))%4),b2=((od>>(2*j))%4);
if(a[i][j]==1) {
if(!(b1|b2))add(od,v);
} else if(!a[i][j]) {
if(!(b1|b2)) {
add(od,v);
if((a[i][j+1]!=1)&&(a[i+1][j]!=1))add(od+inv[j-1]+inv[j],v+1),add(od+2*inv[j-1]+2*inv[j],v+1);
} else if(b1&&b2) {
if(b1==b2)add(od-inv[j-1]*b1-inv[j]*b2,v+1);
} else if(b1) {
if(a[i+1][j]!=1)add(od,v+1);
if(a[i][j+1]!=1)add(od-inv[j-1]*b1+inv[j]*b1,v+1);
} else if(b2) {
if(a[i][j+1]!=1)add(od,v+1);
if(a[i+1][j]!=1)add(od+inv[j-1]*b2-inv[j]*b2,v+1);
}
} else {
if(!(b1|b2)) {
if(a[i][j+1]!=1)add(od+inv[j]*(a[i][j]-1),v+1);
if(a[i+1][j]!=1)add(od+inv[j-1]*(a[i][j]-1),v+1);
} else if(b1&&b2);
else if(b1) {
if(b1==a[i][j]-1)add(od-inv[j-1]*b1,v+1);
} else {
if(b2==a[i][j]-1)add(od-inv[j]*b2,v+1);
}
}
}
}
}
int ans=-1;
for(int k=1; k<=cnt[op]; k++)if(!to[op][k])Min(ans,dp[op][k]);
if(ans==-1)puts("0");
else printf("%d\n",ans-2);
}
return 0;
}