二分搜索-HihoCoder1139
题目描述:
由于自己“想得太多”,导致自己读了半天题才理解了题意。我还以为索敌值会随着每一次到达战略点而减小,结果题意是索敌值是固定了的,并不会改变。
如下是我对题目中第一个案例的分析:
每个圆圈代表一个战略点的编号,边上的值代表每两个战略点之间所需索敌值。
开始时Nettle在1,他只需要3个索敌值就能到达5了,如图:从战略点1到达战略点5只需要 3 个索敌值,因此答案 3 满足条件,再从战略点2到战略点5只需要 2 个索敌值,答案3大于2,因此答案 3 满足条件。所以,Nettle需要的最少索敌值为3,经1---->2---->5路线,可以消灭boss。
代码实现:
#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];///满足k>=val的边 vector<node> G[MAXN];///满足k>=val的边对应的点 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){///广搜从1到BOSS点的路径 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>0){///保证索敌值始终大于零,一直广搜总会找到一个合适的索敌值 q.push(P(G[top.first][i].to,top.second-1));///每次执行后使索敌值-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();///满足val>=k的边对应的点 } ///满足锁敌值为x的边存在now数组里面 for(int i=0;i<2*M;i++){ if(E[i].val<=x){///如果这条边的索敌值比答案x的索敌值小,就放入now数组里面 now[cnt++]=E[i]; } } for(int i=0;i<cnt;i++){ G[now[i].from].push_back((node){now[i].to,now[i].val}); } if(bfs(1,K)){///广搜从1到BOSS点的路径 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; }