满分做法:
题目概述:连通块联通的最小代价。但本题要求选过的点不能再选,所以要选(2*(tot-1))个点((tot)为连通块个数),这波选点就需要贪心了。
首先在每个连通块中选出最小权值的点,并把剩下的点全部加到队列里排序,再选出(tot-2)个点即可。判(impossible)就是剩下的够不够(tot-2)个即可。
这样选的点联通性显然可行(虽然我不会证明),就这样记住就行了吧!
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<queue>
#include<vector>
using namespace std;
typedef long long ll;
const int maxm=100007;
int n,m;
int tot;//连通块个数
int w[maxm],f[maxm];
int cnt[maxm];
vector<int> t,q[maxm];
ll ans=0;
int find(int x)
{
if(x!=f[x])
f[x]=find(f[x]);
return f[x];
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&w[i]),f[i]=i;
for(int i=1;i<=m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
x++;//编号初始为0,++变1
y++;
int fx=find(x);
int fy=find(y);
if(fx!=fy) f[fx]=fy;
}
for(int i=1;i<=n;i++)
{
int fx=find(i);
if(!cnt[fx]) cnt[fx]=++tot;
q[cnt[fx]].push_back(w[i]);
}
if(tot==1)//如果只有一个连通块,直接可行
{
printf("0
");
return 0;
}
for(int i=1;i<=tot;i++)
{
sort(q[i].begin(),q[i].end());
ans+=q[i][0];
for(int j=1;j<q[i].size();j++)
{
t.push_back(q[i][j]);
}
}
if(t.size()<tot-2)
{
printf("Impossible
");
return 0;
}
sort(t.begin(),t.end());
for(int i=0;i<tot-2;i++)
ans+=t[i];
printf("%lld
",ans);
return 0;
}