1002 Dragon slayer
题意:N*M的二维图中给出S,T,还有K堵墙。求从S到T所需要穿过最少墙的数量是多少
(穿墙只是为了方便理解)
思路:N,M,K最大不超过15。将需要穿过的墙的编号二进制压缩。枚举所有可能出现的情况。求最小需要穿过墙的数量。dfs+剪枝
代码:
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
int mov[4][2] = {{0,-1},{1,0},{0,1},{-1,0}};
int tu[40][40], f[60007];
int n,m,k,sx,sy,tx,ty;
bool vis[40][40];
struct Edge//存墙
{
int a,b,c,d;
}e[20];
int egde(int x)//将边和点均化成点
{
return x * 2;
}
int id(int x)
{
return x * 2 + 1;
}
int mak(int x)//计数
{
int cnt = 0;
while(x){cnt += x % 2; x /= 2;}
return cnt;
}
bool dfs(int x,int y,int key)
{
if(x == tx && y == ty)return true;
//printf("<%d %d>",x,y);
vis[x][y] = 1;
for(int p = 0; p < 4; p++)
{
int i = x + mov[p][0];
int j = y + mov[p][1];
if(i < 0 || i > n || j < 0 || j > m || vis[i][j] || tu[i][j])continue;
if(dfs(i, j, key))return true;
}
return false;
}
void init(int s)
{
memset(vis, 0, sizeof vis);
memset(tu, 0, sizeof tu);//拆掉所有墙
for(int i = 0; i < k; i++)
{
if((s & (1<<i)) == 0)//把不需要穿过的墙建好
{
for(int x = e[i].a; x <= e[i].c; x++)
for(int y = e[i].b; y <= e[i].d; y++)
tu[x][y] = 1;
}
}
}
int main()
{
int T;scanf("%d",&T);
while(T--)
{
memset(tu, 0, sizeof tu);
scanf("%d%d%d",&n,&m,&k);
n = egde(n); m = egde(m);
scanf("%d%d%d%d",&sx,&sy,&tx,&ty);
sx = id(sx); sy = id(sy); tx = id(tx); ty = id(ty);
for(int i = 0; i < k; i++)
{
int a,b,c,d; scanf("%d%d%d%d",&a,&b,&c,&d);
a = egde(a); b = egde(b); c = egde(c); d = egde(d);
if(a > c && b == d)swap(a, c);
if(a == c && b > d)swap(b, d);
e[i] = {a,b,c,d};
}
int ans = k;
memset(f, 0, sizeof f);
for(int i = 0; i < (1 << k); i++)
{
if(f[i]) continue;
init(i);
f[i] = dfs(sx, sy, i);
if(f[i])
{
ans = min(ans, mak(i));
for(int j = 0; j < k; j++)
f[i|(1<<j)] |= f[i];
//剪枝,如果状态i所需的穿过的墙可以达到终点,则多几面墙的话也可以。
}
}
printf("%d\n",ans);
}
return 0;
}