对半搜索是二分搜索的一种简单应用。实现原理十分简单:以对半方式分割有序表,根据mid的值决定下一次选择左或者右分割区间,重复操作直至mid等于查找元素或者left大于right为止,其原理很容易理解,在此不赘述。
在复习完二分搜索后,想在oj上找一道题练练手,于是选择了下面的这道noj1053恶魔城这一道题。题目如下:
恶魔城
时间限制(普通/Java) :
1000 MS/ 3000 MS 运行内存限制 : 65536 KByte
总提交 : 340 测试通过 : 71
总提交 : 340 测试通过 : 71
比赛描述
上帝需要创造一位战士去消灭撒旦,这位战士必须要穿过恶魔城才能与撒旦决斗。恶魔城内有M条连接N个路口(从1到N编号)的街道,每一条街道都是单向的(也就是说你不能逆着该街道指定的方向走),并且在城内无论怎么走都不可能走回原来走过的地方。开始的时候,战士的生命力(HP)为INITHP、站在1号路口,而撒旦在第N号路口等待着他。每一条街道上都有许多魔鬼,但是也有一些街道已经被上帝派去的天使占领了。当战士经过连接i号向j号路口的街道时,如果占领该街道的是恶魔,那么他的HP先加倍然后减少L[i,j],我们记为A[i,j]=-L[i,j];如果占领该街道的是天使,那么他的HP就会先加倍然后增加L[i,j],我们记为A[i,j]=+L[i,j];如果该街道不存在,那么A[i,j]=0。如果某一时刻战士的HP<=0,那么他会死亡。因为这个战士将非常无敌,当他见到撒旦的时候只要还活着,就能一口气把撒旦消灭,所以上帝不希望让他的INITHP过高。
你的任务:给定N,A[1..N,1..N],求最小的INITHP,使这个战士能够活着见到撒旦。
你的任务:给定N,A[1..N,1..N],求最小的INITHP,使这个战士能够活着见到撒旦。
输入
文件第一行有一个正整数N(3 ≤ N ≤ 100),下面跟着的第i行第j个数为A[i,j](绝对值不超过10000的整数)。
输出
输出所求最小的INITHP。
样例输入
4
0 -4 0 -10
0 0 +3 0
0 0 0 -14
0 0 0 0
样例输出
4
问题分析:该问题是一道dfs和二分搜索相结合的题目,以二分法确定hp的最小,再根据dfs判定hp是否符合要求。这一类题目还是做得太少,得多加训练。下面贴上AC了的代码及代码注释:
//该题是一道较为容易理解的dfs 题 它的主要变化是hp的控制 使用二分搜索实现最小hp的求取
//dfs+二分搜索
#include<stdio.h>
#define MAX_P 100
int a[MAX_P][MAX_P]; //二维数组记录地图状态
bool vis[MAX_P]; //vis数组记录是否被访问 因题目要求不可以重复走一条路线
bool dfs(int n, int pos, int hp) //dfs判断能否结束撒旦
{
if(hp<=0) //一开始是0的话也会死
return 0;
if(pos==n-1 || hp>10000) //a[pos][i]<10000,hp>=10000,那么hp*2+a[pos][i]肯定大于0
return 1;
vis[pos]=1; //从该点出发 标记该点访问为1表示已访问
for(int i=0; i<n; i++)
{
if(a[pos][i]!=0 && !vis[i] && dfs(n,i,hp*2+a[pos][i]))
return 1;
}
vis[pos]=0; //若从该点出发没有到达终点的路 则将该点标记为未访问过 返回上层递归调用
return 0;
}
int getMinHP(int n)//使用二分法获取最小的hp的值
{
int low=0, high=10000,mid;//初始化
while(low<high) //二分法结构
{
mid = (low+high)>>1;
for(int i=0; i<n; i++) //每次调用需初始化vis数组
vis[i] = 0;
if(dfs(n,0,mid)) //如果hp为mid时能够到达终点 则在0-mid之间继续找小的hp值
high = mid;
else //如果不能 则增大mid的值 在mid到 high之间寻找hp可行的值
low = mid+1;
}
return low;
}
int main()
{
int i,j,n;
scanf("%d",&n);
for(i=0; i<n; i++)
for(j=0; j<n; j++)
scanf("%d",&a[i][j]);
printf("%d\n",getMinHP(n));
}
题目不是很复杂,二分法应用问题还是较多,需要多做此方面的训练。特记下,留待来日回顾。