題目描述:
有N個點,M條邊
邊有權值和顏色(黑色 白色)
最多選K條白邊,黑色邊數量不限,問在保證選出的邊讓圖聯通的情況下,邊取值和最大
題目分析:
由於黑色邊沒有限制數量
我們可以先把所有的黑色邊扔進去,做克魯斯卡爾
然後把白邊按照權值降序排列
第一次我們先儘量保證圖聯通,因此當邊鏈接的點不在一個連通塊的時候取這條邊。記錄當前用白邊的數量
然後判斷能否用少於等於K讓圖聯通
如果白邊還有剩餘,我們就儘量把沒用過的白邊權值大的加進去
題目鏈接:
AC代碼:
#include <iostream>
#include <cstdio>
#include <algorithm>
#define int long long
const int maxn=5e4+100;
const int maxm=5e5+100;
struct node{
int w,u,v;
};
int fa[maxm],used[maxm];
node edge1[maxm],edge2[maxm];
int n,m,k;
int cnt1,cnt2;
inline bool comp(node x,node y){return x.w>y.w;}
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
inline void work()
{
scanf("%lld%lld%lld",&n,&m,&k);
cnt1=cnt2=0;
for(int i=1;i<=n;i++) fa[i]=i,used[i]=0;
int tot=n;
for(int i=1;i<=m;i++)
{
int u,v,w,c;
scanf("%lld%lld%lld%lld",&u,&v,&w,&c);
if(c==0) edge1[++cnt1]=(node){w,u,v};
else edge2[++cnt2]=(node){w,u,v};
}
int ans=0;
for(int i=1;i<=cnt1;i++)
{
int fa1=find(edge1[i].u),fa2=find(edge1[i].v);
ans+=edge1[i].w;
if(fa1!=fa2)
{
fa[fa1]=fa2;
tot--;
}
}
std::sort(edge2+1,edge2+cnt2+1,comp);
int now=0;
for(int i=1;i<=cnt2&&now!=k;i++)
{
if(tot==1) break;
int fa1=find(edge2[i].u),fa2=find(edge2[i].v);
if(fa1!=fa2)
{
now++;
used[i]=1;
fa[fa1]=fa2;
tot--;
ans+=edge2[i].w;
}
}
if(tot!=1)
{
printf("-1\n");
return;
}
for(int i=1;i<=cnt2&&now!=k;i++)
if(!used[i])
{
now++;
ans+=edge2[i].w;
}
printf("%lld\n",ans);
}
signed main()
{
int t;
scanf("%lld",&t);
while(t--) work();
return 0;
}