hihoCoder 1139 二分·二分答案
二分·二分答案
在上一回和上上回裡我們知道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
解題思路:
在這個題目中我們需要找的是路徑最長邊。比如存在一條路徑{1, p[1], p[2], ... , p[j], T}, p = {p[1],p[2],...,p[j]]}, j < K,我們需要找的為 D(P) = Max{w(1, p[1]), w(p[j], T), Max{w(p[i], p[i+1]) | 1 <= i < j} }。則這道題的結果為找出所有從1到T的路徑P',求的Min{D(P')}。由於給定的圖存在環,所以要枚舉出所有1到T的路徑是很難的,因此我們需要換個角度去思考這個問題。
這道題結果有什麼特殊性?
不妨假設答案為j,如果艦隊滿足j以上的索敵值,那麼一定存在至少一條路徑可以從1到T,並且路徑數量小於K。
並且如果艦隊索敵值小於j,則在K條路徑的條件下一定無法從1到T。否則j就不是最小值了。
則對於索敵值滿足這樣一個關係:
可以看出,j值剛好是是否存在路徑的一個分界線。如果我們列舉一個j':
-
j'<j,無法到達boss點
-
j'>=j,一定可以到達boss點
不妨設f(x) = true(索敵值為x時,可以達到boss點), false(索敵值為x時,不能達到boss點)
1.從小到大列舉j',當出現第一個f(k')=true時,j'=j。需要列舉j次,若j很大時,該演算法很低效。
2.二分列舉,設定列舉區間[L,R],滿足f(L)=false, f(R)=true。每次取中間值Mid=(L+R)/2,若f(Mid)=true,令R=Mid;否則令L=Mid。
當L+1=R時,可以知道R即為我們需要尋找的j。
AC程式碼:
#include <bits/stdc++.h>
using namespace std;
struct edge{
int x,w;
};
int n,m,k,t;
vector<edge> v[10005];
int vis[10005];
bool bfs(int dis){
memset(vis,0,sizeof(vis));
queue<int> q;
q.push(1);
while(!q.empty()){
int cur = q.front();
q.pop();
if(cur == t)
return true;
if(vis[cur] == k)
continue;
int l = v[cur].size();
for(int i = 0; i < l; i++){
int tmp = v[cur][i].x;
if(vis[tmp] || v[cur][i].w > dis)
continue;
vis[tmp] = vis[cur]+1;
q.push(tmp);
}
}
return false;
}
int main(){
while(~scanf("%d%d%d%d",&n,&m,&k,&t)){
int a,b,c,maxn = 0;
for(int i = 0; i < m; i++){
scanf("%d%d%d",&a,&b,&c);
edge tmp;
tmp.x = b;tmp.w = c;
v[a].push_back(tmp);
tmp.x = a;
v[b].push_back(tmp);
maxn = max(maxn,c);
}
int mid,l = 0,r = maxn;
while(l <= r){
mid = (l+r)>>1;
if(bfs(mid))
r = mid-1;
else
l = mid+1;
}
printf("%d\n",l);
}
return 0;
}