题意:
有向环覆盖。最小权值。
思路:
拆点法,每个点i拆成Xi,Yi,原图中的u->v对应二分图中的Xu -> Yv,把权值取反,就转化为最大权完美匹配了。
Ps: 注意重边!
#include <cstdio>
#include <queue>
#include <vector>
#include <cstring>
#include <algorithm>
#define fi first
#define se second
#define pii pair<int,int>
using namespace std;
const int INF = 0x3f3f3f3f;
typedef long long LL;
const int maxn = 100+5;
int n;
int G[maxn][maxn];
int lx[maxn], ly[maxn];
int cx[maxn], cy[maxn];
int visx[maxn], visy[maxn];
int slack[maxn];
bool GetAugumentPath(int u){
visx[u] = 1;
for(int i = 0; i < n; ++i){
if(visy[i]) continue;
int d = lx[u] + ly[i] - G[u][i];
if(d == 0){
visy[i] = 1;
if(cy[i] == -1||GetAugumentPath(cy[i])){
cx[u] = i;
cy[i] = u;
return true;
}
}
else
slack[i] = min(slack[i], d);
}
return false;
}
void km(){
for(int x = 0; x < n; ++x){
for(int i = 0; i < n; ++i) slack[i] = INF;
while(1){
memset(visx, 0, sizeof(visx));
memset(visy, 0, sizeof(visy));
if(GetAugumentPath(x)) break;
int d = INF;
for(int i = 0; i < n; ++i) if(!visy[i]) d = min(d, slack[i]);
for(int i = 0; i < n; ++i) if(visx[i]) lx[i] -= d;
for(int i = 0; i < n; ++i){
if(visy[i]) ly[i] += d;
else slack[i] -= d;
}
}
}
}
int solve(){
memset(cx, -1, sizeof(cx));
memset(cy, -1, sizeof(cy));
for(int i = 0; i < n; ++i) lx[i] = -INF;
for(int i = 0; i < n; ++i){
for(int j = 0; j < n; ++j){
lx[i] = max(lx[i], G[i][j]);
ly[j] = 0;
}
}
km();
int ans = 0;
for(int i = 0; i < n; ++i){
if(cx[i] == -1||G[i][cx[i]] == -INF) return -1;
ans += G[i][cx[i]];
}
return -ans;
}
int main()
{
//freopen("in.txt","r",stdin);
while(scanf("%d",&n) == 1&&n){
for(int i = 0; i < n; ++i)
for(int j = 0; j < n; ++j) G[i][j] = -INF;
for(int i = 0; i < n; ++i){
int b,d;
while(scanf("%d",&b)==1&&b){
scanf("%d",&d);
//printf("%d %d ",b,d);
G[i][b-1] = max(G[i][b-1], -d); // 应对重边!!!
}
}
int ans = solve();
if(ans == -1) printf("N\n");
else printf("%d\n", ans);
}
return 0;
}