NC218425 魏迟燕的自走棋(贪心+并查集)
第一步贪心,按照权值排序,越大的能用肯定最优
因为在当前情况下,假如可以用这个但不用
最后求取的解,你将他对应的那个换掉,一个不会更差。
排完序后我们再考虑本题。
这题巧妙地转化在于,转化成图模型,每个武器的两个端点相当于一条边,如果k=1就是自环
对于一个大小为x的集合,他们中的边要不是x-1,要不是x(如果没有一个武器能给某个点,这个点没有意义不讨论)
如果边等于x-1,相当于这个有x个人的集合里面有x-1个武器,因此一个人是空闲的,且我们可以通过边传递武器,使得任何一个空闲都可以
那么边等于x代表没人空闲。
我们按大到小枚举边的时候,就相当于考虑并查集的思路,如果他们本来已经在一个集合里面了,就看他们是不是还可以加一条边
如果他们不再一个集合,那么如果两个集合有一个还可以加就可以加
#include<bits/stdc++.h> using namespace std; typedef long long ll; typedef pair<int,int> pll; const int N=2e5+10; const int inf=0x3f3f3f3f; const int mod=1e9+7; int p[N],st[N]; int n,m; struct node{ int a,b,x; }s[N]; int find(int x){ if(p[x]!=x){ p[x]=find(p[x]); } return p[x]; } bool cmp(node a,node b){ return a.x>b.x; } int main(){ ios::sync_with_stdio(false); cin>>n>>m; int i; for(i=1;i<=n;i++){ p[i]=i; st[i]=0; } for(i=1;i<=m;i++){ int k; cin>>k; int a,b,c; if(k==1){ cin>>a>>c; s[i]={a,a,c}; } else{ cin>>a>>b>>c; s[i]={a,b,c}; } } sort(s+1,s+1+m,cmp); ll sum=0; for(i=1;i<=m;i++){ int a=s[i].a,b=s[i].b,c=s[i].x; int pa=find(a),pb=find(b); if(pa!=pb&&(!st[pa]||!st[pb])){ p[pb]=pa; sum+=c; if(st[pa]||st[pb]){ st[pa]=1; } } else if(!st[pa]){ st[pa]=1; sum+=c; } } cout<<sum<<endl; return 0; }
没有人不辛苦,只有人不喊疼