poj3133之經典插頭DP

Manhattan Wiring
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
*/
 



發佈了314 篇原創文章 · 獲贊 26 · 訪問量 50萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章