hihoCoder #1139 : 二分·二分答案 (二分搜索+BFS)

#1139 : 二分·二分答案

时间限制:10000ms

单点时限:1000ms

内存限制:256MB

描述

在上一回和上上回里我们知道Nettle在玩《艦これ》,Nettle在整理好舰队之后终于准备出海捞船和敌军交战了。
在这个游戏里面,海域是N个战略点(编号1..N)组成,如下图所示

其中红色的点表示有敌人驻扎,猫头像的的点表示该地图敌军主力舰队(boss)的驻扎点,虚线表示各个战略点之间的航线(无向边)
在游戏中要从一个战略点到相邻战略点需要满足一定的条件,即需要舰队的索敌值大于等于这两点之间航线的索敌值需求。
由于提高索敌值需要将攻击机、轰炸机换成侦察机,舰队索敌值越高,也就意味着舰队的战力越低。
另外在每一个战略点会发生一次战斗,需要消耗1/K的燃料和子弹。必须在燃料和子弹未用完的情况下进入boss点才能与boss进行战斗,所以舰队最多只能走过K条航路。
现在Nettle想要以最高的战力来进攻boss点,所以他希望能够找出一条从起始点(编号为1的点)到boss点的航路,使得舰队需要达到的索敌值最低,并且有剩余的燃料和子弹。

特别说明:两个战略点之间可能不止一条航线,两个相邻战略点之间可能不止一条航线。保证至少存在一条路径能在燃料子弹用完前到达boss点。

提示:你在找什么?

输入

第1行:4个整数N,M,K,T。N表示战略点数量,M表示航线数量,K表示最多能经过的航路,T表示boss点编号, 1≤N,K≤10,000, N≤M≤100,000
第2..M+1行:3个整数u,v,w,表示战略点u,v之间存在航路,w表示该航路需求的索敌值,1≤w≤1,000,000。

输出

第1行:一个整数,表示舰队需要的最小索敌值。

样例输入

5 6 2 5
1 2 3
1 3 2
1 4 4
2 5 2
3 5 5
4 5 3

样例输出

3

题目大意:有n个节点,m条边,每一条边上有一个“锁敌值w”。我们现在要从节点1前往节点n,最多只能经过k条边。从a节点到b节点,需要我所拥有的“锁敌值w”大于a和b之间的“锁敌值w”。题目保证节点1到节点n至少有一条路径能够到达,求我所拥有的最小的“锁敌值”是多少?

思路:题目求取的是最小的“锁敌值w”,所以从节点1到节点n的路径不一定是最短的。但是这些路径都需要满足一个条件——我所拥有的“锁敌值”大于路径中任何一条边的锁敌值。题目保证节点1到节点n至少有一条路径能够到达,因此我们可以去二分的遍历“锁敌值w”,通过深度优先搜索或者宽度优先搜索是否能找到一条能够从节点1到节点n的路径的条件,来不断的缩小范围,最后得到我们需要的最小的“锁敌值w”。

AC代码:

#include <cstdio>
#include <vector>
#include <cstring>
#include <queue>
#include <algorithm>
using namespace std;
const int MAXN = 1e6;
const int INF = 1000000+10;
int N,M,K,T;
struct edge{
	int from;//源点
	int to;//目标点
	int val;//锁敌值
};

struct node{
	int to;//目标点
	int val;//锁敌值
};
edge E[MAXN*2];//所有的边
edge now[MAXN*2];//满足val>=k的边
vector<node> G[MAXN];//满足val>=k的边对应的点
bool used[MAXN];
typedef pair<int,int> P;
queue<P> q;
int cmp(edge a, edge b){
	return a.val<b.val;//从小到大
}

bool bfs(int num,int k){
	while(!q.empty()){
		q.pop();
	}
	q.push(P(num,k));
	used[num]=true;
	while(!q.empty()){
		P top = q.front();
		q.pop();
		if(top.first==T){
			return true;
		}
		for(int i=0;i<G[top.first].size();i++){
			if(!used[G[top.first][i].to]){
				used[G[top.first][i].to]=true;
				if(top.second>=1){
					q.push(P(G[top.first][i].to,top.second-1));
				}
			}
		}
	}
	return false;
}

bool C(int x){
	int cnt=0;
	memset(used,0,sizeof(used));
	for(int i=0;i<MAXN;i++){
		G[i].clear();
	}
	//满足锁敌值为x的边存在now数组里面
	for(int i=0;i<2*M;i++){
		if(E[i].val<=x){
			now[cnt++]=E[i];
		}
	}
	for(int i=0;i<cnt;i++){
		G[now[i].from].push_back((node){now[i].to,now[i].val});
	}
	//广搜从1到BOSS点的路径
	if(bfs(1,K)){
		return true;
	}
	return false;
}

void solve(){
	int lb=0,ub=INF;
	while(ub-lb>1){
		int mid=(ub+lb)/2;
		//满足锁敌值为x,且能够找到1点到BOSS点的路径
		if(C(mid)){
			ub=mid;
		}else{
			lb=mid;
		}
	}
	printf("%d\n",ub);
	
}


int main(){
	while(~scanf("%d%d%d%d",&N,&M,&K,&T)){
		for(int i=0;i<M;i++){
			int a,b,val;
			scanf("%d%d%d",&a,&b,&val);
			E[i].from=a;E[i].to=b;E[i].val=val;
			E[i+M].from=b;E[i+M].to=a;E[i+M].val=val;
		}
		sort(E,E+2*M,cmp);
		solve();
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Rainbow_storm/article/details/81146021
今日推荐