题意简述
给定一个 n n n 个点 m m m 条边的带权图,定义一棵生成树的权重为所有其边的权重之和模 k k k。求这个图的生成树的最小权重。
解题思路
发现 n n n 最大只有 8 8 8。又有经典结论,一个 n n n 个点的完全图共有 n n − 2 n^{n-2} nn−2 个最小生成树,所以直接暴搜是可以过的。
考虑在暴搜的同时维护一个并查集,加第 i i i 条边时,如果这条边连接的两个点已经联通了,就不能加这条边。这是一个很有效的剪枝。
代码示例
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,m,k;
struct node{
int u,v,w;
}a[110];
int ans=0x3f3f3f3f3f3f3f3f,num[20],fa[20],vis[110]={0};
int getfa(int x){
if(x==fa[x]) return x;
return getfa(fa[x]);
}
void dfs(int step,int lst){
if(step==n-1){
int anss=0;
for(int i=1;i<=step;i++) (anss+=a[num[i]].w)%=k;
ans=min(ans,anss);
return;
}
for(int i=lst;i<=m;i++){
int fau=getfa(a[i].u),fav=getfa(a[i].v);
if(fau==fav||vis[i]) continue;
int lstv=fa[fav];
vis[i]=1;
fa[fav]=fau;
num[step+1]=i;
dfs(step+1,i+1);
vis[i]=0;
fa[fav]=lstv;
}
}
signed main(){
cin>>n>>m>>k;
for(int i=1;i<=n;i++) fa[i]=i;
for(int i=1;i<=m;i++) cin>>a[i].u>>a[i].v>>a[i].w;
dfs(0,1);
cout<<ans<<endl;
return 0;
}