Kaitou Kid - The Phantom Thief (2)
Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 1220 Accepted Submission(s): 427
Problem Description
破解字迷之后,你得知Kid将会在展览开始后T分钟内盗取至少一颗宝石,并离开展馆。整个展馆呈矩形分布,划分为N*M个区域,有唯一的入口和出口(不能从出口进入,同样不能从入口出去)。由某个区域可直接移动至相邻四个区域中的一个,且最快需要一分钟。假设Kid进入放有宝石的区域即可盗取宝石,无需耗时。问至少要封锁几个区域(可以封锁放有宝石的区域,但不能封锁入口和出口)才能保证Kid无法完成任务。
Input
输入的第一行有一个整数C,代表有C组测试数据。每组测试数据的第一行有三个整数N,M,T(2<=N,M<=8,T>0)。接下来N行M列为展馆布置图,其中包括:
'S':入口
'E':出口
'J':放有宝石的区域,至少出现一次
'.':空白区域
'#':墙
'S':入口
'E':出口
'J':放有宝石的区域,至少出现一次
'.':空白区域
'#':墙
Output
对每组测试数据,输出至少要封锁的区域数。
Sample Input
2 5 5 5 SJJJJ ..##J .JJJJ .J... EJ... 5 5 6 SJJJJ ..##J .JJJJ .J... EJ...
Sample Output
0 2
Author
LL
Source
【题意】:给出一个入口和一个出口,问能否堵住某些区域使得Kid不能在T时间内偷到宝石并走出去
【思路】:初看似乎只能暴搜,将所有的BFS一遍,不过思考之后就能发现,最多只要4个障碍物,就能将出口或者入口堵住,所以,方法就是:
封锁出口或者入口周围的格子.
最多需要4个封锁点.
所以我们可以采取这样的策略:
1.寻找一条盗贼的可行路线,如果没有,返回0.
2.计算封锁出口和入口四周需要的封锁点数量,取小的一个,假设是k,k <=4
3.从少到多,遍历所有封锁点个数小于k的方案,验证是否是一条有效的覆盖方案
(可以通过是否阻止了1中的盗贼线路进行快速验证).
如果有有效覆盖方案,返回这个方案的覆盖点值,否则继续.
4.如果没有比k小的覆盖方案,返回k.
#include<stdio.h>
#include<iostream>
#include<string>
#include<string.h>
#include<cstdlib>
#include<algorithm>
#include<map>
#include<cmath>
#include<stack>
#include<queue>
#include<set>
#include<vector>
#define F first
#define S second
#define PI acos(-1.0)
#define E exp(1.0)
#define INF 0xFFFFFFF
#define MAX -INF
#define len(a) (__int64)strlen(a)
#define mem0(a) (memset(a,0,sizeof(a)))
#define mem1(a) (memset(a,-1,sizeof(a)))
using namespace std;
template<class T> T gcd(T a, T b) {
return b ? gcd(b, a % b) : a;
}
template<class T> T lcm(T a, T b) {
return a / gcd(a, b) * b;
}
template<class T> inline T Min(T a, T b) {
return a < b ? a : b;
}
template<class T> inline T Max(T a, T b) {
return a > b ? a : b;
}
int n, m;
struct node {
int x, y, step, rx[64], ry[64], num;
//x,y表示坐标,num表示是否拿到宝石,step表示步数或者时间,rx[]和ry[]分别保存到达该点的路径
};
node cur; //起始点
char mp[10][10];
int ans;
int dx[] = { 0, 0, 1, -1 };
int dy[] = { 1, -1, 0, 0 };
int tt;
int vis[10][10][2]; //三维数组,第三维表示该点是否已拿到过宝石
queue<node> q;
void dfs(int deep) {
if (deep > ans) //总共最多只需封锁四个区域,即入口或出口的四个方向
return;
while (!q.empty())
q.pop();
q.push(cur);
node t;
int sum = -1;
mem0(vis);
vis[cur.x][cur.y][0] = 1;
while (!q.empty()) {
t = q.front();
q.pop();
node nex;
if (mp[t.x][t.y] == 'E' && t.num) { //能走到出口
sum = t.step;
break;
}
for (int i = 0; i < 4; i++) {
int tx = t.x + dx[i];
int ty = t.y + dy[i];
if (tx >= n || tx < 0 || ty >= m || ty < 0 || mp[tx][ty] == '#'
|| t.step >= tt)
continue;
if (mp[tx][ty] == 'J') {
nex.num = 1;
} else {
nex.num = t.num;
}
if (vis[tx][ty][nex.num])
continue;
for (int k = 1; k <= t.step; k++) { //记录路径
nex.rx[k] = t.rx[k];
nex.ry[k] = t.ry[k];
}
vis[tx][ty][nex.num] = 1;
nex.x = tx;
nex.y = ty;
nex.step = t.step + 1;
nex.rx[nex.step] = tx;
nex.ry[nex.step] = ty;
q.push(nex);
}
}
if (sum == -1) //sum==-1表示该封锁区域设置成功,kid无法完成任务
{
if (deep < ans)
ans = deep;
return;
}
for (int i = 1; i < t.step; i++) {
char cc = mp[t.rx[i]][t.ry[i]]; //保存原先的地图
if (cc == 'E' || cc == 'S') //入口或出口不能封锁
continue;
mp[t.rx[i]][t.ry[i]] = '#';
dfs(deep + 1); //设置一个封锁区域后,继续遍历
mp[t.rx[i]][t.ry[i]] = cc; //将地图还原
}
}
int main() {
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
int T;
scanf("%d", &T);
while (T--) {
scanf("%d%d%d", &n, &m, &tt);
for (int i = 0; i < n; i++) {
scanf("%s", mp[i]);
for (int j = 0; j < m; j++) {
if (mp[i][j] == 'S')
cur.x = i, cur.y = j;
}
}
cur.num = 0;
cur.step = 0;
ans = 4;
dfs(0);
printf("%d\n", ans);
}
return 0;
}