[题目描述]
由于地震使得连接汶川县城电话线全部损坏,假如你是负责将电话线接到震中汶川县城的负责人,汶川县城周围分布着N(1≤N≤1,000)根按1..N 顺次编号的废弃的电话线杆,任意两根电话线杆间都没有电话线相连。一共P(1≤P≤10,000)对电话线杆间可以拉电话线,其余的由于地震使得无法被连接。
第i 对电话线杆的两个端点分别为Ai,Bi,它们间的距离为Li(1≤Li≤1,000,000)。数据中保证每对(Ai,Bi)最多只出现1 次。编号为1 的电话线杆已经接人了全国的电话网络,整个县城的电话线全都连到了编号为N 的电话线杆上。也就是说,你的任务仅仅是找一条将1 号和N 号电话线杆连起来的路径,其余的电话线杆并不一定要连人电话网络。
电信公司决定支援灾区免费为汶川县城连结K(0≤K<N)对由你指定的电话线杆。对于此外的那些电话线,需要为它们付费,总费用等于其中最长的电话线的长度(每根电话线仅连接一对电话线杆)。如果需要连接的电话线杆不超过K 对,那么总支出为0。
请你计算一下,将电话线引到震中汶川县城最少需要在电话线上花多少钱?
[输入格式]
输入文件的第一行包含三个用空格隔开的整数:N,P 和K。
第二行到第P+1 行:每行分别都为空格隔开的整数:Ai,Bi 和Li。
[输出格式]
输出文件中仅包含一个整数,表示在这项工程上的最小支出。如果任务不可能完成,则输出-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
[输出样例]
4
凭着个人做题经验,关于图的问题大多是最短路,最小生成树,网络流等等。只不过题会给你出的面目全非,让你看不出算法是啥,所以这时候呢,我们就想办法往自己学过的算法上靠。
(以上仅代表自己想的)
可以用二分的方法。二分答案,然后对于每一次二分得到的答案,将图中所有大于这个数的边的边权设为1,否则设为0。这样图就变成了一个边权只有0和1的图了。
然后再跑一遍最短路。得到的1到n节点的距离如果大于k,就说明我们要让电信公司连接的电线杆多了,也就是说这个答案小了,于是在右子区间里找;否则就在左子区间里找。
我觉得这个算法的精妙之处是在于转化成最短路的想法:就是边权变为0或1,这样跑最短路的时候就统计出了比ans大的边有几条,进而继续二分。
乍一看最短路的定义改成了路径上边权最大的边最小,但最终还是一个普通的最短路。所以可见最短路这个算法是一个很成熟的算法,或说是一个固定的算法。我们要去做的不是去更改这个算法,而是想办法将其他问题转化成可以用这个算法解决的问题。
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #include<iostream> 5 #include<cstring> 6 #include<cmath> 7 #include<vector> 8 #include<queue> 9 #include<cctype> //isdigit 10 using namespace std; 11 typedef long long ll; 12 #define enter printf("\n") 13 const int maxn = 1e4 + 5; 14 const int INF = 0x3f3f3f3f; 15 inline ll read() 16 { 17 ll ans = 0; 18 char ch = getchar(), last = ' '; 19 while(!isdigit(ch)) {last = ch; ch = getchar();} 20 while(isdigit(ch)) 21 { 22 ans = ans * 10 + ch - '0'; ch = getchar(); 23 } 24 if(last == '-') ans = -ans; 25 return ans; 26 } 27 inline void write(ll x) 28 { 29 if(x < 0) {putchar('-'); x = -x;} 30 if(x == 0) {putchar('0'); return;} 31 int q[100], N = 0; 32 q[1] = 0; 33 while(x) {q[++N] = x % 10; x /= 10;} 34 while(N) {putchar('0' + q[N]); --N;} 35 } 36 37 int n, P, k; 38 int p[maxn]; 39 void init(int n) 40 { 41 for(int i = 1; i <= n; ++i) p[i] = i; 42 } 43 int Find(int x) 44 { 45 return x == p[x] ? x : p[x] = Find(p[x]); 46 } 47 void merge(int x, int y) 48 { 49 int px = Find(x), py = Find(y); 50 if(px == py) return; 51 p[px] = py; return; 52 } 53 54 vector<int> v[maxn], c[maxn]; 55 struct Node 56 { 57 int x, y, co; 58 }edges[maxn]; 59 int dis[maxn]; 60 bool done[maxn]; 61 void init_(int n) 62 { 63 for(int i = 1; i <= n; ++i) 64 { 65 v[i].clear(); c[i].clear(); 66 dis[i] = done[i] = 0; 67 } 68 } 69 bool check(int x) 70 { 71 init_(n); 72 for(int i = 1; i <= P; ++i) 73 { 74 if(edges[i].co > x) 75 { 76 v[edges[i].x].push_back(edges[i].y); c[edges[i].x].push_back(1); 77 v[edges[i].y].push_back(edges[i].x); c[edges[i].y].push_back(1); 78 } 79 else 80 { 81 v[edges[i].x].push_back(edges[i].y); c[edges[i].x].push_back(0); 82 v[edges[i].y].push_back(edges[i].x); c[edges[i].y].push_back(0); 83 } 84 } 85 for(int i = 1; i <= n; ++i) dis[i] = INF; 86 queue<int> q; q.push(1); 87 done[1] = 1; dis[1] = 0; 88 while(!q.empty()) 89 { 90 int now = q.front(); q.pop(); done[now] = 0; 91 for(int i = 0; i < (int)v[now].size(); ++i) 92 { 93 if(dis[now] + c[now][i] < dis[v[now][i]]) 94 { 95 96 dis[v[now][i]] = dis[now] + c[now][i]; 97 if(!done[v[now][i]]) 98 { 99 q.push(v[now][i]); 100 done[v[now][i]] = 1; 101 } 102 } 103 } 104 } 105 return dis[n] <= k ? true : false; 106 } 107 int Max = 0; 108 109 int main() 110 { 111 freopen("phone.in", "r", stdin); 112 freopen("phone.out", "w", stdout); 113 n = read(); P = read(); k = read(); 114 if(P <= k) {printf("0\n"); return 0;} 115 init(n); 116 for(int i = 1; i <= P; ++i) 117 { 118 edges[i].x = read(), edges[i].y = read(), edges[i].co = read(); 119 merge(edges[i].x, edges[i].y); 120 Max = max(Max, edges[i].co); 121 } 122 if(Find(1) != Find(n)) {printf("-1\n"); return 0;} //并查集判断一下图的连通性 123 int L = 0, R = Max; 124 while(L < R) 125 { 126 int mid = (L + R) >> 1; 127 if(check(mid)) R = mid; 128 else L = mid + 1; 129 } 130 write(L); enter; 131 return 0; 132 }