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