题意:在一个有向图中找到一些环,使得图上的每个点都在这些环中的某个环中出现过一次。
难度:2
题解:二分图最优匹配(模板再次TLE)。把每个点i拆成Xi和Yi,新增一个附加源s和一个附加汇t,s连每一个Xi以及每一个Yi连t一条容量为1,费用为0的边;对于每一条边的关系:u->v,从Xu向Yv连一条容量为1,费用为该边权值的边,求最大流。
难度:2
题解:二分图最优匹配(模板再次TLE)。把每个点i拆成Xi和Yi,新增一个附加源s和一个附加汇t,s连每一个Xi以及每一个Yi连t一条容量为1,费用为0的边;对于每一条边的关系:u->v,从Xu向Yv连一条容量为1,费用为该边权值的边,求最大流。
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
using namespace std;
int n,m;
const int maxn = 205;
const int INF = 0xffffff;
int w[maxn][maxn];
int lx[maxn],ly[maxn];
int linky[maxn];
int visx[maxn],visy[maxn];
int slack[maxn];
bool find(int x) {
visx[x] = true;
for(int y=1;y<=n;y++) {
if(visy[y])
continue;
int t = lx[x]+ly[y]-w[x][y];
if(t == 0) {
visy[y] = true;
if(linky[y] == -1 || find(linky[y])) {
linky[y] = x;
return true;
}
}
else if(slack[y] > t)
slack[y] = t;
}
return false;
}
int KM() {//返回最优匹配得知
int i,j;
memset(linky,-1,sizeof(linky));
memset(ly,0,sizeof(ly));
for(i=1;i<=n;i++)
for(j=1,lx[i]=-INF;j<=n;j++)
if(w[i][j] > lx[i])
lx[i] = w[i][j];
for(int x=1;x<=n;x++) {
for(i=1;i<=n;i++)
slack[i] = INF;
while(true) {
memset(visx,0,sizeof(visx));
memset(visy,0,sizeof(visy));
if(find(x))
break;
int d = INF;
for(i=1;i<=n;i++) {
if(!visy[i] && d > slack[i])
d = slack[i];
}
for(i=1;i<=n;i++) {
if(visx[i])
lx[i] -= d;
}
for(i=1;i<=n;i++) {
if(visy[i]) ly[i] += d;
else slack[i] -= d;
}
}
}
int result = 0;
for(i=1;i<=n;i++) {
if(linky[i] == -1 || w[linky[i]][i] == -INF)
return 1;
else
result += w[linky[i]][i];
}
return result;
}
int main() {
int tcase;
int i,j;
cin>>tcase;
while(tcase--) {
cin>>n>>m;
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
w[i][j]=-INF;
int a,b,c;
while(m--) {
cin>>a>>b>>c;
if(w[a][b] < -c)
w[a][b] = -c;
}
cout<<-KM()<<endl;
}
return 0;
}