USACO 2008 January Silver Telephone Lines /// 二分最短路 邻接表dijkstra oj22924
题目大意:
一共有N (1 ≤ N ≤ 1,000)个电线杆,有P P (1 ≤ P ≤ 10,000)对电线杆是可以连接的,
用几条线连接在一起的电线杆之间都可相互通信,现在想要使得电线杆1和电线杆N能相互通信,
并且电线公司提出KK (0 ≤ K < N)条电线是可以免费使用的,
当使用电线的数量超过K条,超出的电线要收费,
收的总费用为去掉免费使用的K条电线之后最长的那条电线的长度。
问最少费用是多少
Input
* Line 1: Three space-separated integers: N, P, and K
* Lines 2..P+1: Line i+1 contains the three space-separated integers: Ai, Bi, and Li
Output
Line 1: A single integer, the minimum amount Farmer John can pay. If it is impossible to connect the farm to the phone company, print -1.
Sample Input
5 7 1
1 2 5
3 1 4
2 4 8
3 2 3
5 2 9
3 4 7
4 5 6
Sample Output
4
/* 这题二分猜测答案 用最短路dijkstra验证 验证 猜测的mid是1~N的最短路中第k+1大的花费 用dis[i]记录1到i点时 花费>= mid 的顶点数 //若1~N没有路 则dis[n]始终为初始值INF //则二分猜测的答案始终正确 最终二分会被推到最右的点 即无路 输出-1 //若1~N有路但小于k+1条 则dis[n]始终<k+1 //则二分猜测的答案始终错误 最终二分会被推到最左的点 即无花费 输出0 */ #include <bits/stdc++.h> #define INF 0x3f3f3f3f using namespace std; struct NODE { /// 要放在vector里 需要重载小于号 按 val即花费 升序排序 int to,val; // 到to点 花费val bool operator<(const NODE& x) const { return val < x.val; } }; vector <NODE> vec[1005]; int n,p,k,vis[1005],dis[1005]; bool dijk(int mid) { for(int i=1;i<=n;i++) vis[i]=0, dis[i]=INF; priority_queue <NODE> q; ///优先队列自动升序排序 NODE e; e.to=1,e.val=0; q.push(e); dis[1]=0; while(!q.empty()) { e=q.top(); q.pop(); vis[e.to]=0; vector <NODE> ::iterator it; for(it=vec[e.to].begin();it!=vec[e.to].end();it++) { NODE tmp=*it; if(vis[tmp.to]) continue; int nowv=dis[e.to]+ (tmp.val>=mid ? 1:0); /// tmp.val>=mid的话 说明在这条路上 到顶点tmp.to的路大于猜测值 /// 则dis[该点]为dis[上一点]+1 否则+0 先用nowv记录 /// 与答案mid相等的也记录下来 若mid为正确答案 则恰好为第k+1个 /// 可能有多个花费等于mid 所以判断dis[n]>=k+1 if(dis[tmp.to]>nowv) {/// 之前记录过的比nowv大 说明nowv所在的这条才是较短的路 dis[tmp.to]=nowv; vis[tmp.to]=1; q.push(tmp); } } } //printf("%d\n",dis[n]); return dis[n]>=k+1; } int main() { while(~scanf("%d%d%d",&n,&p,&k)) { for(int i=1;i<=n;i++) vec[i].clear(); NODE e; while(p--) { int a,b,c; scanf("%d%d%d",&a,&b,&c); e.to=b, e.val=c; vec[a].push_back(e); e.to=a, e.val=c; vec[b].push_back(e);/// 无向图邻接表 } int le=0,rig=1000005,ans=-1; while(le<=rig) {// 二分答案 mid为猜测值 进入dijk()验证 mid是不是第k+1条路 int mid=(le+rig)/2; if(spfa(mid)) le=mid+1, ans=mid; ///mid在k个之后(太小或恰好) else rig=mid-1; ///mid在k个之内(太大) 缩小范围继续验证 } if(ans>1000000) ans=-1; /// 说明dis[n] 一直在k个之后 if(rig<0) ans=0; /// 说明dis[n] 一直在k个之内 printf("%d\n",ans); } return 0; }