首页 > 代码库 > poj3133之经典插头DP
poj3133之经典插头DP
Time Limit: 5000MS | Memory Limit: 65536K | |
Total Submissions: 1482 | Accepted: 869 |
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.
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题意:求连接两个2和两个3的路径之和最小,输出和-2,不存在就输出0
输入0表示可以走,1表示不可以走
分析:按照插头DP的模式进行DP,碰到0时如果p=q=0可以选择不走
碰到2和3的时候保证只有一个插头即可
这题有很多细节要注意
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <string> #include <queue> #include <algorithm> #include <map> #include <cmath> #include <iomanip> #define INF 99999999 typedef long long LL; using namespace std; const int MAX=30000+10; const int maxn=100000+10; const int N=10+10; int n,m,size[2],index,bit[N]; int head[MAX],next[maxn]; LL dp[2][maxn],state[2][maxn],sum; int mp[N][N]; void HashCalState(LL s,LL num){ int pos=s%MAX; for(int i=head[pos];i != -1;i=next[i]){ if(state[index][i] == s){ dp[index][i]=min(dp[index][i],num); return; } } state[index][size[index]]=s; dp[index][size[index]]=num; next[size[index]]=head[pos]; head[pos]=size[index]++; } void DP(){ //初始化 sum=INF; index=0; size[index]=1; state[index][0]=0; dp[index][0]=0; //逐格进行DP for(int i=1;i<=n;++i){ for(int k=0;k<size[index];++k)state[index][k]<<=2;//上一行最后的空插头减去,本行最前面增加一个空插头,所以*4 for(int j=1;j<=m;++j){ memset(head,-1,sizeof head); index=index^1; size[index]=0; for(int k=0;k<size[index^1];++k){ LL s=state[index^1][k],temp; LL num=dp[index^1][k]; int p=(s>>bit[j-1])%4; int q=(s>>bit[j])%4; int w=(s>>bit[j+1])%4; if(mp[i][j] == 1){//该格不能走 if(!p && !q)HashCalState(s,num); continue; }else if(!p && !q){ if(!mp[i][j]){//该点是0需要两个插头或者不走 HashCalState(s,num);//该点是0可以不走 if(mp[i][j+1] == 1 || mp[i+1][j] == 1)continue;//右边或下边相邻的格子不能到达,无法完成两个插头 if(mp[i][j+1]+mp[i+1][j] == 5)continue;//表示添加的两个插头两端将是2和3,不能到达 if(mp[i][j+1] == 2 || mp[i+1][j] == 2){//有点是2则必须连得插头是1(表示连2的线) temp=s+(1<<bit[j-1])+(1<<bit[j]); if(w == 0)HashCalState(temp,num+3);//mp[i][j+1]没有插头则经过该点路径长度+1,即num+2+1 else if(w == 1 && mp[i][j+1] != 2)HashCalState(temp,num+2);//增加的路径只有[i+1,j]和[i,j] } else if(mp[i][j+1] == 3 || mp[i+1][j] == 3){//有点是3则必须连得插头是2(表示连3的线) temp=s+2*(1<<bit[j-1])+2*(1<<bit[j]); if(w == 0)HashCalState(temp,num+3); else if(w == 2 && mp[i][j+1] != 3)HashCalState(temp,num+2);//特别要注意w != 0时要判断[i,j+1]是否是2或3,防止2/3的情况下有两个插头 } else { if(w == 0){ HashCalState(s+(1<<bit[j-1])+(1<<bit[j]),num+3); HashCalState(s+2*(1<<bit[j-1])+2*(1<<bit[j]),num+3); }else if(w == 1 && mp[i][j+1] != 2)HashCalState(s+(1<<bit[j-1])+(1<<bit[j]),num+2); else if(w == 2 && mp[i][j+1] != 3)HashCalState(s+2*(1<<bit[j-1])+2*(1<<bit[j]),num+2); } } else if(mp[i][j] == 2){//只能有独立插头 if(mp[i][j+1] != 1 && mp[i][j+1] != 3){ if(w == 0)HashCalState(s+(1<<bit[j]),num+2); else if(w == 1 && mp[i][j+1] != 2)HashCalState(s+(1<<bit[j]),num+1); } if(mp[i+1][j] != 1 && mp[i+1][j] != 3)HashCalState(s+(1<<bit[j-1]),num+2); } else if(mp[i][j] == 3){//只能有独立插头 if(mp[i][j+1] != 1 && mp[i][j+1] != 2){ if(w == 0)HashCalState(s+2*(1<<bit[j]),num+2); else if(w == 2 && mp[i][j+1] != 3)HashCalState(s+2*(1<<bit[j]),num+1); } if(mp[i+1][j] != 1 && mp[i+1][j] != 2)HashCalState(s+2*(1<<bit[j-1]),num+2); } }else if(!p && q){ if(mp[i][j]){ //if(mp[i][j] != q+1)continue; s=s-q*(1<<bit[j]); HashCalState(s,num); }else{ if(mp[i][j+1] == 0 || mp[i][j+1] == q+1){ if(w == 0)HashCalState(s,num+1); else if(w == q && mp[i][j+1] == 0)HashCalState(s,num); } if(mp[i+1][j] == 0 || mp[i+1][j] == q+1){ s=s+q*(1<<bit[j-1])-q*(1<<bit[j]); HashCalState(s,num+1); } } }else if(p && !q){ if(mp[i][j]){ //if(mp[i][j] != p+1)continue; s=s-p*(1<<bit[j-1]); HashCalState(s,num); }else{ if(mp[i+1][j] == 0 || mp[i+1][j] == p+1)HashCalState(s,num+1); if(mp[i][j+1] == 0 || mp[i][j+1] == p+1){ s=s-p*(1<<bit[j-1])+p*(1<<bit[j]); if(w == 0)HashCalState(s,num+1); else if(w == p && mp[i][j+1] == 0)HashCalState(s,num); } } }else if(p == q/*&& !mp[i][j]*/){//p == q == 1或者p == q == 2时p‘,q‘不能有插头 s=s-p*(1<<bit[j-1])-q*(1<<bit[j]); HashCalState(s,num); } } } } //cout<<size[index]<<endl; for(int k=0;k<size[index];++k)sum=min(sum,dp[index][k]); } int main(){ for(int i=0;i<N;++i)bit[i]=i<<1; while(~scanf("%d%d",&n,&m),n+m){ for(int i=0;i<N;++i)for(int j=0;j<N;++j)mp[i][j]=1; for(int i=1;i<=n;++i){ for(int j=1;j<=m;++j)cin>>mp[i][j]; } DP();//插头DP if(sum == INF)sum=2; printf("%lld\n",sum-2); } return 0; } /* 5 9 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 2 0 0 0 0 0 2 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 0 0 5 6 0 0 0 0 0 0 0 0 0 3 0 0 0 2 0 0 0 2 0 0 0 3 0 0 0 0 0 0 0 0 */