题目链接:Q - Tour HDU - 3488
题意:找出若干个环覆盖所有点,并且总边权和最小。
节点个数
200
200
200,边数
30000
30000
30000,可能含有重边。
因为要找若干个不相交的环,而且题目保证一定有解,故说明最后的选择中一定是每个点被出,入一次,故可将一个点拆为入点和出点,出点在左侧,入点在右侧,若没有边权的限制就是二分图匹配了,加上边权的限制就是带权二分图匹配,因为要求最小带权,故需要将边权取反,求负权边的最小匹配。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=309;
int w[maxn][maxn];
int la[maxn],lb[maxn];
bool va[maxn],vb[maxn];
int match[maxn];
int n,delta,upd[maxn];
bool dfs(int x){
va[x]=1;
for(int y=1;y<=n;++y)
if(!vb[y])
if(la[x]+lb[y]-w[x][y]==0){
vb[y]=1;
if(!match[y]||dfs(match[y])){
match[y]=x;
return 1;
}
}
else upd[y]=min(upd[y],la[x]+lb[y]-w[x][y]);
return 0;
}
int KM(){
delta=0;
for(int i=1;i<=n;++i){
la[i]=-(1<<30);
lb[i]=0;
match[i]=0;
for(int j=1;j<=n;++j)
la[i]=max(la[i],w[i][j]);
}
for(int i=1;i<=n;++i)
while(1){
memset(va,0,sizeof(va));
memset(vb,0,sizeof(vb));
delta=1<<30;
for(int j=1;j<=n;++j) upd[j]=1e9;
if(dfs(i)) break;
for(int j=1;j<=n;++j)
if(!vb[j]) delta=min(delta,upd[j]);
for(int j=1;j<=n;++j){
if(va[j]) la[j]-=delta;
if(vb[j]) lb[j]+=delta;
}
}
int ans=0;
for(int i=1;i<=n;++i) ans+=w[match[i]][i];
return ans;
}
const int inf=0x3f3f3f3f;
int main(){
int t,m,u,v,W;
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j) w[i][j]=-inf;
while(m--){
scanf("%d%d%d",&u,&v,&W);
w[u][v]=max(w[u][v],-W);
}
printf("%d\n",-KM());
}
return 0;
}