题目
题目描述
Farmer John wants to set up a telephone line at his farm. Unfortunately, the phone company is uncooperative, so he needs to pay for some of the cables required to connect his farm to the phone system.
There are N (1 ≤ N ≤ 1,000) forlorn telephone poles conveniently numbered 1..N that are scattered around Farmer John‘s property; no cables connect any them. A total of P (1 ≤ P ≤ 10,000) pairs of poles can be connected by a cable; the rest are too far apart.
The i-th cable can connect the two distinct poles Ai and Bi, with length Li (1 ≤ Li ≤ 1,000,000) units if used. The input data set never names any {Ai, Bi} pair more than once. Pole 1 is already connected to the phone system, and pole N is at the farm. Poles 1 and N need to be connected by a path of cables; the rest of the poles might be used or might not be used.
As it turns out, the phone company is willing to provide Farmer John with K (0 ≤ K < N) lengths of cable for free. Beyond that he will have to pay a price equal to the length of the longest remaining cable he requires (each pair of poles is connected with a separate cable), or 0 if he does not need any additional cables.
Determine the minimum amount that Farmer John must pay.
【洛谷翻译太愚蠢被我删了~】
输入输出格式
输入格式:
输入文件的第一行包含三个数字n,p,k;
第二行到第p+1行,每行分别都为三个整数ai,bi,li。
输出格式:
一个整数,表示该项工程的最小支出,如果不可能完成则输出-1.
输入输出样例
输入样例#1:
5 7 1 1 2 5 3 1 4 2 4 8 3 2 3 5 2 9 3 4 7 4 5 6
输出样例#1:
4
分析
【瞎扯】
不好意思我又要瞎扯了,还是提醒各位审题!
拿到题目看了一遍然后默默地想起了【BZOJ 2763 飞行路线】(别找了这题我压根儿就没过)。题面完全一毛一样的!然后所以这是分层图?等下我们图论专题才刚开始上了半天就给我做分层图?哦是吗我要投诉清北学堂。
【然后我去向老师吐槽了一下】
老师:
……又没看题目
【正经】
首先我们来一起读题【雾
As it turns out, the phone company is willing to provide Farmer John with K (0 ≤ K < N) lengths of cable for free. Beyond that he will have to pay a price equal to the length of the longest remaining cable he requires (each pair of poles is connected with a separate cable), or 0 if he does not need any additional cables.
本题最重要的一句话↑,它告诉我们,电信公司【移动:?】愿意支付其中k条电话线的费用,其余的电话线中,FJ支付最贵的一条(电信公司真差劲,下次找移动【电信:?】)。简单想一下:贪心的想法,最佳情况,我们找前k贵的让电信公司付钱,第k+1贵的让FJ付钱。
假设我们有一个check(int c)来检查当我们支付价格为c的电话线时,我们是否能成功让整个线路运行起来。有了它我们就可以肆无忌惮地二分了!原因很简单:显然是单调的且我们有check()来帮我们检查可行性。
那么本题的终点就转移到了如何写出check()函数。
假如当前我们二分到mid,我们将所有权值大于mid的所有电话线的权值全部设置为1(意味着我们需要消耗一次【电话线抵用券】来假设电话线),而权值小于mid则全部设置为0(我们不需要让FJ付款,也不需要让电信掏钱)。对于这个0/1的图跑SPFA,如果我们发现最短路径长度大于k,意味着这个k然而并不可行,我们缩小左边界继续二分;若我们发现它小于等于k,说明我们仍然有可能有更优解,缩小右边界继续二分。
本题的一大重点还是搞清楚left/mid/right的关系,或者说这就是二分的一个难点吧。
程序
1 #include <bits/stdc++.h> 2 using namespace std; 3 const int MAXN = 2000, MAXM = 20000; 4 int n, m, k, Head[MAXN], Edge_Count, ans, G[MAXN][MAXN]; 5 struct edge 6 { 7 int Next, From, Aim, Weight; 8 }Edge[MAXM]; 9 void insert(int u, int v, int w) 10 { 11 Edge[++Edge_Count] = (edge){Head[u], u, v, w}; 12 Head[u] = Edge_Count; 13 } 14 bool check(int Len) 15 { 16 // 初始化G数组为-1,-1代表没有边相连 17 for (int i = 1; i < MAXN; i++) 18 for (int j = 1; j < MAXN; j++) 19 G[i][j] = -1; 20 memset(dis,0x3F,sizeof(dis)); 21 for (int i = 1; i <= Edge_Count; i++) 22 if (Edge[i].Weight > Len) 23 G[Edge[i].From][Edge[i].Aim] = 1, G[Edge[i].Aim][Edge[i].From] = 1; 24 else 25 G[Edge[i].From][Edge[i].Aim] = 0, G[Edge[i].Aim][Edge[i].From] = 0; 26 // 0:有边相连,且权值小于Len 27 // 1:有边相连,且权值大于Len,消耗一次电话线抵用券 28 // 以下标准SPFA 29 queue<int> Q; 30 Q.push(1); 31 int dis[MAXN]; 32 dis[1] = 0; 33 bool vis[MAXN]; 34 vis[1] = true; 35 while (!Q.empty()) 36 { 37 int u = Q.front(); 38 Q.pop(); 39 for (int i = 1; i <= n; i++) 40 { 41 if (G[u][i] != -1 && dis[u]+G[u][i] < dis[i]) 42 { 43 dis[i] = dis[u]+G[u][i]; 44 if (!vis[i]) 45 vis[i] = true, Q.push(i); 46 } 47 } 48 vis[u] = false; 49 } 50 // 若最少的小号次数大于k,这种情况不可信,返回假 51 // 否则说明是一个可行解,但不一定是最优解,返回真继续二分 52 if (dis[n] > k) 53 return false; 54 else 55 return true; 56 } 57 int main() 58 { 59 freopen("tele.in","r",stdin); 60 cin >> n >> m >> k; 61 for (int i = 1; i <= m; i++) 62 { 63 int u, v, w; 64 cin >> u >> v >> w; 65 insert(u,v,w); 66 insert(v,u,w); 67 } 68 int l = 0, r = 1000000000, mid; 69 bool flag = false; 70 while (l < r) 71 { 72 mid = (l+r)/2; 73 if (check(mid)) 74 r = mid, flag = true; 75 else 76 l = mid + 1; 77 } 78 // flag说明我们是否找到过可行的解 79 // 若没有,输出-1 80 if (flag) 81 cout << l << endl; 82 else 83 cout << -1 << endl; 84 return 0; 85 }
原文地址:https://www.cnblogs.com/OIerPrime/p/8443352.html