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