=== ===
这里放传送门
=== ===
题解
一开始有一个比较明显的思路就是先把一级边单独排序然后往里加加到k条停下然后剩下的再用二级边补起来。但是这样做对不对呢?选择的一级边一定是最小的k条吗?有没有可能选一条稍微大一点的一级边,然后这样就能选进去一条稍微小一点的二级边然后达到降低最小边的目的呢?
然后ATP就开始画。。。画了一会儿以后感觉是不可能的。。
因为这种做法的本质是用某两条边替换掉了另外两条边,那么假如一开始选的两条边是A1和A2,后来替换掉的是B1和B2,那么肯定有B1>=A1并且B2
<
<script type="math/tex" id="MathJax-Element-107"><</script>A2。又因为替换完了以后最大边变小了,所以原来的最大边是A2,那么就有A2>=B1。
因为要求的是一个最小生成树,所以当A1和A2没有加进去的时候图中有三个连通块。又因为这里涉及到了4条不同的边,那么肯定有至少一对边连接了两个相同的连通块,它们不能同时被选。
那么对这一对不能同时被选的边进行讨论。假如A1和B2可以同时被选,那么选A1和B2肯定更优,因为A1是两条一级边中的较小者,B2是两条二级边中的较小者;假如A1和B2不能同时被选,因为A2>B1,而B1>=B1对应的那条二级边,所以选择A1和B1对应的那条二级边肯定是最优的。不管怎样都不可能跳过A1选择B1。
也就是说选择的一级边一定是能选的最小k条,所以这种做法是正确的。
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,m,k,father[10010],sum,cnt;
struct edge{
int x,y,w;
edge(int X=0,int Y=0,int W=0){x=X;y=Y;w=W;}
}e1[20010],e2[20010];
int comp(edge a,edge b){return a.w<b.w;}
int find(int x){
if (x==father[x]) return x;
father[x]=find(father[x]);
return father[x];
}
int main()
{
scanf("%d%d%d",&n,&k,&m);
for (int i=1;i<=m;i++){
int x,y,c1,c2;
scanf("%d%d%d%d",&x,&y,&c1,&c2);
e1[i]=edge(x,y,c1);
e2[i]=edge(x,y,c2);
}
sort(e1+1,e1+m+1,comp);
sort(e2+1,e2+m+1,comp);
for (int i=1;i<=n;i++) father[i]=i;
for (int i=1;i<=m;i++){
int r1=find(e1[i].x),r2=find(e1[i].y);
if (r1!=r2){
father[r1]=r2;++cnt;
sum=max(sum,e1[i].w);
}
if (cnt==k) break;
}
for (int i=1;i<=m;i++){
int r2=find(e2[i].x),r1=find(e2[i].y);
if (r1!=r2){
father[r1]=r2;
sum=max(sum,e2[i].w);
}
}
printf("%d\n",sum);
return 0;
}