HDU 1983 BFS+DFS

本文详细解析了HDU 1983问题的解决方案,采用贪婪算法策略,通过计算封锁点数量来阻止盗贼逃逸,介绍了算法的时间复杂度和实现细节。
封锁出口或者入口周围的格子. 
最多需要4个封锁点. 
所以我们可以采取这样的策略: 
1.寻找一条盗贼的可行路线,如果没有,返回0. 
2.计算封锁出口和入口四周需要的封锁点数量,取小的一个,假设是k,k <=4 
3.从少到多,遍历所有封锁点个数小于k的方案,验证是否是一条有效的覆盖方案
(可以通过是否阻止了1中的盗贼线路进行快速验证). 
如果有有效覆盖方案,返回这个方案的覆盖点值,否则继续. 
4.如果没有比k小的覆盖方案,返回k. 
时间复杂度: 
最多(M*N)^3次有效覆盖验证.即(8*8)^3=256k次.其中有很大一部分可以通过快速验证排除(取决于1的路径长短,所以一般1应该求出最短路径的可行路线)

   
// 187ms 248kb
#include < iostream >
#include
< queue >
#include
< string >
using namespace std;
int n,m,t,ans;
int dir[ 4 ][ 2 ] = { 1 , 0 , - 1 , 0 , 0 , - 1 , 0 , 1 };
char map[ 10 ][ 10 ];
bool vis[ 2 ][ 10 ][ 10 ];
struct node
{
int x,y,t,k; // x,y为坐标,t为时间,k为是否有碰到宝石
int rox[ 64 ],roy[ 64 ]; // 用来保存路径的,每个节点都保存有一条路径,只有到达终点后才有完整的路径
};
node start,end,temp,
in ;

queue
< node > Q;

void dfs( int deep)
{
if (deep > ans) return ;
int i,j,minstep = - 1 ;
node q;
while ( ! Q.empty()) // 清空队列
Q.pop();
Q.push(start);
// 起始位置入队
memset(vis, false , sizeof (vis)); // 初始化标记数组
vis[ 0 ][start.x][start.y] = true ; // 标记起始点为真
while ( ! Q.empty()) // 从起点开始寻找一条路径
{
q
= Q.front();
Q.pop();
if (q.t > t)
continue ;
if (q.k && map[q.x][q.y] == ' E ' ) // 找到出口
{
minstep
= q.t;
break ;
}
for (i = 0 ;i < 4 ;i ++ ) // 分别从四个方向开始扫描
{
int xx = q.x + dir[i][ 0 ];
int yy = q.y + dir[i][ 1 ];
if (xx < 0 || xx >= n || yy < 0 || yy >= m || map[xx][yy] == ' # ' )
continue ; // 越界或碰到墙
if (map[xx][yy] == ' J ' )
in .k = 1 ; // 碰到珠宝
else
in .k = q.k; // 没有碰到则标记为前一个状态
if ( ! vis[ in .k][xx][yy])
{
vis[
in .k][xx][yy] = true ;
for (j = 1 ;j <= q.t;j ++ ) // 将前一节点保存的路径加入到现在的节点,由于是按顺序的,所以他会形成连贯的路线
{
in .rox[j] = q.rox[j];
in .roy[j] = q.roy[j];
}
in .x = xx;
in .y = yy;
in .t = q.t + 1 ;
// 这个节点的第in.t步保存的是xx,yy
in .rox[ in .t] = xx;
in .roy[ in .t] = yy;
Q.push(
in );
}
}
}
if (minstep == - 1 )
{
// minstep == -1 表明在t时间内即使不用设置关卡也不能成功逃离
if (deep < ans)
ans
= deep;
return ;
}
char cc;
for (i = 1 ;i < q.t;i ++ )
{
cc
= map[q.rox[i]][q.roy[i]];
if (cc == ' S ' || cc == ' E ' )
continue ;
map[q.rox[i]][q.roy[i]]
= ' # ' ; // 在做完前面的bfs后,这里的q是到达终点的节点,因此他完整的保存了一条路
dfs(deep + 1 );
map[q.rox[i]][q.roy[i]]
= cc ;
}
}
void inits()
{
int i,j;
memset(vis,
false , sizeof (vis));
for (i = 0 ;i < n;i ++ )
for (j = 0 ;j < m;j ++ )
{
if (map[i][j] == ' S ' )
{
start.x
= i;start.y = j;
start.t
= 0 ;start.k = 0 ;
break ;
}
}
ans
= 4 ;
dfs(
0 );
printf(
" %d\n " ,ans);
}
int main()
{
int i,cas;
scanf(
" %d " , & cas);
while (cas -- )
{
scanf(
" %d%d%d " , & n, & m, & t);
for (i = 0 ;i < n;i ++ )
scanf(
" %s " ,map[i]);
inits();
}
return 0 ;
}

转载于:https://www.cnblogs.com/laipDIDI/articles/2048962.html

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值