简单来说题目就是给定一个\(n\times m\)的网格图,同行边权相同,同列边权相同,求该网格图的最小生成树。
根据Kruskal算法的贪心思想,我们要优先选择权值尽可能小的行,并将这条边应用于尽可能多的列。列方向同理。
为了保证最终生成树的连通性,我们显然要先把最小的行和最小的列全都选上,如下图所示:
可以发现,如果当前已经选择了\(x\)组行方向上的边,此时如果选列方向上的边,则最多能放\(n-x\)条。列方向同理。
根据贪心思想,接下来我们选择第\(3\)行,此时我们最多能放\(8-1=7\)条边,将其全部选择。
根据贪心思想,接下来我们选择第\(6\)列(或第\(2\)列),此时我们最多能放\(5-2=3\)条边,将其全部选择。
我们发现最多只能放\(3\)条,否则一定会出现环。
……
有了上面的结论,我们就可以写出代码了。将行和列的权值分别排序,先各选去一个最小值,再用两个指针分别指向两个数组的第\(2\)为,每次选权值最小的行/列即可。
点击查看代码
#include<bits/stdc++.h>
#define int long long
#define N 300010
using namespace std;
int n,m,a[N],b[N],posa,posb,ans,cnt;
signed main(){
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=m;i++) cin>>b[i];
sort(a+1,a+1+n);
sort(b+1,b+1+m);
posa=1,posb=1;
ans=a[1]*(m-1)+b[1]*(n-1),cnt=n+m-2;
while(1){
if(a[posa+1]<b[posb+1]){
ans+=a[++posa]*(m-posb),cnt+=(m-posb);
}else{
ans+=b[++posb]*(n-posa),cnt+=(n-posa);
}
if(cnt==n*m-1) break;
}
cout<<ans<<"\n";
return 0;
}