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。
下附代码:
先将题意转化一下,最多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; }
相关文章推荐
- C# System.Reflection
- SpringMVC文件上传实现
- 计算机网络各层协议
- 不同网段IP地无法Ping通的问题
- Hive元数据解析
- 面向对象特征
- jsp中的out对象 和 servlet中的response.getOutputStream()
- 关于RSA加密算法的长度限制问题
- jQuery通过jquery.form.js插件使用AJAX提交Form表单
- PHP中的__call和__callStatic方法
- 数据库分表
- 简单的对话聊天窗体组件
- 使用 SOAPUI 执行负载测试
- BestCoder Round #51 (div.2) Zball in Tina Town
- 【机房收费系统】总览
- 【Python学习笔记】-生成验证码图片
- HTML滚动文字代码 marquee标签
- 中介模式和学习日记Effective C++
- PHP中的__set和__get方法
- Android 自定义圆形进度条