您的位置:首页 > 其它

USACO Jan08 (COGS 174) 架设电话线 二分答案,缩点,BFS判断可行性

2015-08-17 09:40 253 查看
这道题让我好好地加深了一下对二分的细节性处理,等会再做反思,先说一下这道题的思路。

先将题意转化一下,最多K条边免费,路径上的最大值为花费,令这个花费最小,K次免费机会当然是尽量用完啦(用不完的时候答案就是0嘛),总结来说就是求1到n的所有路径中,第K+1大的边权的最小值,若有路径上的边数不超过K,答案即0,若没有路径,答案即-1。

看到数据范围只有1000,就可以联想到这道题不会很简单,稍微复杂一点(对于我来说)。

想到二分,二分之后判断可行性,可以直接二分答案,我的做法是把所有边按边权排序,二分的是排序后的编号,这道题的一个还不错的特点就是二分之后答案为mid,可以把小于或等于mid权值的边连的点缩点(因为我二分的是编号,所以缩点的过程比较方便了,直接循环1到mid,令每条边连的点的并查集树根相同就可以),缩点可以用并查集来写(为了方便,最好让1和n作为树根),之后剩下大于mid的边(如果有大于mid的边在一个缩点内就可以忽略它),在这些剩下的边中用BFS求1到n的最短路,如果不大于K,就说明mid是可行的,反之就不可行。

其实也可以不缩点,要理解二分的答案的实际意义,我们假设答案为mid,那么权值小于或等于mid的边是没有任何影响的,想怎么走就怎么走,我们把免费的K次机会用到了大于mid的边上,在那些“想怎么走就怎么走”的边中,mid作为答案,是最大值。

另外,有无解的情况,我的做法是在二分之前先用BFS判断是否有1到n的路径,有些人省略了这一步,直接用二分答案的结果判断,蒟蒻我一开始也想尝试,发现怎么都A不掉就果断放弃了,还是用自己的提前判断吧,不过这样也速度更快吧。

这道题的思路还是很好写的,不过一开始因为自己对二分的理解不够彻底导致WA了几次。首先针对这道题,有一个点答案为0,而我一开始的输出是1,因为我把l初值设为1,之后我的解决方法是在输出答案前加了特判,之后才发现只要把l的初值设为0就可以。

另外,针对二分这个思想的就是,判断了当前答案可行后,令r = mid,不可行的话,l = mid+1,这样一来,二分循环的条件就是while(l < r)输出的答案就直接是l或r了,因为最后l == r。至于我原来逗B的写法就不论述了,太心酸了。写下这句话提醒下自己曾经是多么2。

下附代码:

#include <cstdio>
#include <algorithm>
#include <vector>
#include <queue>
#include <cstring>
using namespace std;

int n, p, k, f[1005], d[1005];
vector <int> G[1005];
queue <int> q;
struct edge{
int u, v, l;
}E[10005];
bool cmp(edge a, edge b){return a.l < b.l;}

int find(int x) {return f[x] = f[x] == x ? x : find(f[x]);}

int main()
{
scanf("%d %d %d", &n, &p, &k);
for(int i = 1; i <= p; i++)
scanf("%d %d %d", &E[i].u, &E[i].v, &E[i].l);
for(int i = 1; i <= p; i++){
int u = E[i].u, v = E[i].v;
G[u].push_back(v);
G[v].push_back(u);
}
q.push(1);
while(q.size()){
int i = q.front(); q.pop();
for(int j = 0; j < G[i].size(); j++){
int y = G[i][j];
if(!d[y]){
d[y] = d[i] + 1;
q.push(y);
}
}
}
if(!d
){
printf("-1");
return 0;
}
sort(E+1, E+p+1, cmp);
int l = 0, r = p, mid;
while(l < r){
mid = (l+r)/2;
memset(G, 0, sizeof G);
memset(d, 0, sizeof d);
for(int i = 1; i <= n; i++) f[i] = i;
for(int i = 1; i <= mid; i++) {
int fu = find(E[i].u), fv = find(E[i].v);
if(fu != fv) {
if(fu == 1 || fu == n) f[fv] = fu;
else if(fv == 1 || fv == n) f[fu] = fv;
else f[fu] = fv;
}
}
for(int i = mid+1; i <= p; i++){
int u = find(E[i].u), v = find(E[i].v);
if(u == v) continue;
G[u].push_back(v);
G[v].push_back(u);
}
q.push(1);
while(q.size()){
int i = q.front(); q.pop();
for(int j = 0; j < G[i].size(); j++){
int y = G[i][j];
if(!d[y] && y != 1){
d[y] = d[i] + 1;
q.push(y);
}
}
}
if(d
<= k) r = mid;
else l = mid+1;
}
printf("%d", E[l].l);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: