Manhattan Wiring
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 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.
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 “ 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 Source |
题目大意:
在一个n*m的矩阵上又一些点能走,有一些点是障碍,有两个编号为2的点,两个编号为3的点,问把编号为2的点连起来所需的最短路径和,编号为3的点连起来。要求一个位置只能经过一次。
解题思路:
矩阵上找路径,自然想到插头dp。不过这里添加了匹配的限制条件,所以路径就有两种,把经过点2的路径叫做路径2,经过点3的路径叫做路径3,由于题目要求的是最短路径,我们在dp的过程中就不需要用最小表示法了,直接用路径的编号表示插头即可。这样虽然会因为没有维护连通性导致出现多余的环,但是不会影响答案。
AC代码:
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <ctime>
#include <vector>
#include <queue>
#include <stack>
#include <deque>
#include <string>
#include <map>
#include <set>
#include <list>
using namespace std;
#define INF 0x3f3f3f3f
#define LL long long
#define fi first
#define se second
#define mem(a,b) memset((a),(b),sizeof(a))
const int MAXN=15;
int N, M, maze[MAXN][MAXN];//0不能走,1可以走,2位标记为2的点,3为标记为3的点
int tmp_state[MAXN];//保存临时轮廓线状态
struct HashMap
{
const static int mod=10007;//小一点
const static int maxn=1000100;//大概状态数大一些
int head[mod];//链表头指针
int next[maxn];//指向链表下一个节点
int size;//当前节点数
int key[maxn],val[maxn];//键,值
void clear()
{
size=0;
memset(head,-1,sizeof head);
}
inline void insert(int _key,int _val)
{
int p=_key%mod;//取模后对应的链
for(int i=head[p];~i;i=next[i])
if(key[i]==_key)
{
val[i]=min(val[i], _val);//找到相同键值的结点后的具体操作根据题意修改
return ;
}
key[size]=_key;
val[size]=_val;
next[size]=head[p];
head[p]=size++;
}
}hm[2];
void decode(int *tmp_state, int m, int key)//把数字解压到数组,M表示矩阵一行的长度
{
for(int i=m;i>=0;--i)//这里用2位表示一个插头的状态
{
tmp_state[i]=key&3;
key>>=2;
}
}
int encode(int *tmp_state, int m)//把状态压缩到一个数字
{
int res=0;
for(int i=0;i<=m;++i)
{
res<<=2;
res|=tmp_state[i];
}
return res;
}
void shift(int *tmp_state, int m)//处理完一行时,把竖着的轮廓线转移到下一行
{
for(int i=m;i>0;--i)
tmp_state[i]=tmp_state[i-1];
tmp_state[0]=0;
}
void dp0(int y, int x, bool now)//对不能走的点dp
{
for(int i=0;i<hm[now].size;++i)
{
decode(tmp_state, M, hm[now].key[i]);
if(!tmp_state[x-1] && !tmp_state[x])
{
if(x==M)
shift(tmp_state, M);
hm[!now].insert(encode(tmp_state, M), hm[now].val[i]);
}
}
}
void dp1(int y, int x, int now)//对可以走的点dp
{
for(int i=0;i<hm[now].size;++i)
{
decode(tmp_state, M, hm[now].key[i]);
int left=tmp_state[x-1], up=tmp_state[x];
if(left && up)//两个插头
{
if(left==up)//必须两个插头类型一致才能dp
{
tmp_state[x-1]=tmp_state[x]=0;
if(x==M)
shift(tmp_state, M);
hm[!now].insert(encode(tmp_state, M), hm[now].val[i]+1);
}
}
else if((!left && up) || (left && !up))//只有一个插头
{
int t=left?left:up;
if(maze[y][x+1]==1 || maze[y][x+1]==t)//向右延伸
{
tmp_state[x-1]=0;
tmp_state[x]=t;
hm[!now].insert(encode(tmp_state, M), hm[now].val[i]+1);
}
if(maze[y+1][x]==1 || maze[y+1][x]==t)//向下延伸
{
tmp_state[x-1]=t;
tmp_state[x]=0;
if(x==M)
shift(tmp_state, M);
hm[!now].insert(encode(tmp_state, M), hm[now].val[i]+1);
}
}
else//没有插头
{
if(x==M)
shift(tmp_state, M);
hm[!now].insert(encode(tmp_state, M), hm[now].val[i]);
if(maze[y][x+1] && maze[y+1][x])
{
if(maze[y][x+1]==1 && maze[y+1][x]==1)
{
tmp_state[x-1]=tmp_state[x]=2;//加2号插头
hm[!now].insert(encode(tmp_state, M), hm[now].val[i]+1);
tmp_state[x-1]=tmp_state[x]=3;//加3号插头
hm[!now].insert(encode(tmp_state, M), hm[now].val[i]+1);
}
else if((maze[y][x+1]==1 && maze[y+1][x]==2) || (maze[y][x+1]==2 && maze[y+1][x]==1) || (maze[y][x+1]==2 && maze[y+1][x]==2))
{
tmp_state[x-1]=tmp_state[x]=2;
hm[!now].insert(encode(tmp_state, M), hm[now].val[i]+1);
}
else if((maze[y][x+1]==1 && maze[y+1][x]==3) || (maze[y][x+1]==3 && maze[y+1][x]==1) || (maze[y][x+1]==3 && maze[y+1][x]==3))
{
tmp_state[x-1]=tmp_state[x]=3;
hm[!now].insert(encode(tmp_state, M), hm[now].val[i]+1);
}
}
}
}
}
void dp_edge(int y, int x, int now, int k)//对于端点dp
{
for(int i=0;i<hm[now].size;++i)
{
decode(tmp_state, M, hm[now].key[i]);
int left=tmp_state[x-1], up=tmp_state[x];
if((left==k && up==0) || (left==0 && up==k))//起始端点
{
tmp_state[x-1]=tmp_state[x]=0;
if(x==M)
shift(tmp_state, M);
hm[!now].insert(encode(tmp_state, M), hm[now].val[i]+1);
}
else if(!left && !up)//终止端点
{
if(maze[y][x+1]==1 || maze[y][x+1]==k)
{
tmp_state[x-1]=0;
tmp_state[x]=k;
hm[!now].insert(encode(tmp_state, M), hm[now].val[i]+1);
}
if(maze[y+1][x]==1 || maze[y+1][x]==k)
{
tmp_state[x-1]=k;
tmp_state[x]=0;
if(x==M)
shift(tmp_state, M);
hm[!now].insert(encode(tmp_state, M), hm[now].val[i]+1);
}
}
}
}
void solve()
{
bool now=0;
hm[now].clear();
hm[now].insert(0, 0);
for(int i=1;i<=N;++i)
for(int j=1;j<=M;++j)
{
hm[!now].clear();
if(maze[i][j]==0)
dp0(i, j, now);
else if(maze[i][j]==1)
dp1(i, j, now);
else dp_edge(i, j, now, maze[i][j]);
now^=1;
}
int ans=0;
for(int i=0;i<hm[now].size;++i)
ans+=hm[now].val[i];
if(ans>0)
ans-=2;
printf("%d\n", ans);
}
int main()
{
while(~scanf("%d%d", &N, &M)&&(N||M))
{
mem(maze,0);
for(int i=1;i<=N;++i)
for(int j=1;j<=M;++j)
{
scanf("%d",&maze[i][j]);
if(maze[i][j]<=1)//01交换
maze[i][j]^=1;
}
solve();
}
return 0;
}