一本通1491:Tree

题目传送门

【题目描述】

给你一个无向带权连通图,每条边是黑色或白色。让你求一棵最小权的恰好有 need 条白色边的生成树。题目保证有解。

【输入】

第一行 V,E,need 分别表示点数,边数和需要的白色边数。

接下来 E 行,每行 s,t,c,col 表示这边的端点(点从 0 开始标号),边权,颜色(0 白色,1 黑色)。

【输出】

一行表示所求生成树的边权和。

【输入样例】

2 2 1  
0 1 1 1  
0 1 2 0

【输出样例】

2

【提示】

数据范围:

对于所有数据,V≤5×104,E≤105​​ ,边权为 [1,100] 中的正整数。


首先,这道题很明确地告诉我们要写最小生成树.这里就用kruskal来写.

题目要求need条白边,有限定条件,所以不能写裸的.

那么先挑出need条最短的边,再从黑边里面挑组成最小生成树不就行了?

这样还是不行,因为这样它就干扰了原来的最小生成树,不能保证最优解.

因为kruskal算法的根本是边的权值要单调递增.所以我们可以从这里下手,把每一条白边加上一个值,改变每一条边的排列顺序,最后计算答案时就只需要减去原来加上的值就可以了.由于我们不知道要加上多少,所以我们可以用二分答案来做每一次的判断来得到答案.

注意:如果两条边权值相等,白边的优先级要高一些.

代码如下

#include<bits/stdc++.h>
#define maxn 50005
using namespace std;
int n,m,p,father[maxn],ans,sum,cnt,white;
struct node{
    int u,v,dis,c;
}edge[maxn*4];
inline bool cmp(node x,node y)
{
    if(x.dis==y.dis) return x.c<y.c;//权值相等白边优先级高
    return x.dis<y.dis;
}
inline int find(int x)
{
    if(father[x]!=x) father[x]=find(father[x]);
    return father[x];
}
inline bool check(int x)//二分答案+kruskal
{
    for(int i=0;i<=n;i++) father[i]=i;
    for(int i=1;i<=m;i++)
    {
        if(!edge[i].c) edge[i].dis+=x;//加上x的值
    }
    sort(edge+1,edge+m+1,cmp);
    cnt=0,white=0,sum=0;
    for(int i=1;i<=m;i++)//略作修改的kruskal模板
    {
        if(cnt==n-1) break;
        int x=find(edge[i].u),y=find(edge[i].v);
        if(x==y) continue;
        father[x]=y;
        cnt++;
        sum+=edge[i].dis;
        if(!edge[i].c) white++;
    }
    for(int i=1;i<=m;i++)
    {
        if(!edge[i].c) edge[i].dis-=x;//还原
    }
    if(white>=p) return 1;
    return 0;
}
int main()
{
    scanf("%d%d%d",&n,&m,&p);
    for(int i=1;i<=m;i++) scanf("%d%d%d%d",&edge[i].u,&edge[i].v,&edge[i].dis,&edge[i].c);
    int l=-101,r=101,mid;
    while(l<=r)
    {
        mid=l+r>>1; 
        if(check(mid)) 
        {
            l=mid+1;
            ans=sum-p*mid;
        }
        else r=mid-1;
    } 
    printf("%d",ans);
    return 0;
}
View Code

  

猜你喜欢

转载自www.cnblogs.com/thumbs/p/12274394.html