这道题作为一道regional,最值得学习的应该是问题的等价转换。
把“找出几个圈,使得所有的点都只存在于一个圈内”,转换为“给每个点找一个孩子,使得一个点只有一个父亲”。转换后的问题,就可以用二分图匹配来做了:每个点拆成两部分分别放在二分图的S和T,使得S到T的连线代表儿子-父亲的关系。之后再求二分图的带权值的完美匹配,则可以保证每个结点只有一个儿子、一个父亲的情况下,求出最小费用了。
我自己想的时候就只考虑了从“画圈”上来下手的方法,但是都很麻烦,而且效率不高。通过探索“圆圈”的特性(每个结点只有一个儿子、一个父亲),则可以将问题转换、建模成可以用经典模型解决的问题,这种思路应该多学习和锻炼~
Run Time: 0.146s
#define UVa "LT11-10.1349.cpp" //Optimal Bus Route Design
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
#include<iostream>
using namespace std;
//Global Variables.
const int maxn = 100 + 5, INF = 1<<30;
int n;
////
struct Edge {
int from, to, cap, flow, cost;
Edge(int f, int t, int c, int fw, int ct): from(f), to(t), cap(c), flow(fw), cost(ct) {}
};
struct MCMF {
vector<Edge> edges;
vector<int> G[2*maxn];
int a[2*maxn], p[2*maxn], d[2*maxn], inq[2*maxn];
void init() {
edges.clear();
for(int i = 0; i < 2*maxn; i ++)G[i].clear();
}
void addEdge(int from, int to, int cap, int flow, int cost) {
edges.push_back(Edge(from, to, cap, flow, cost));
edges.push_back(Edge(to, from, 0, flow, -cost));
G[from].push_back(edges.size() - 2);
G[to].push_back(edges.size() - 1);
}
bool bellmanFord(int src, int targ, int& flow, long long& cost) {
for(int i = 0; i <= 2*maxn; i ++) d[i] = INF;
memset(inq, 0, sizeof(inq));
queue<int> q;
q.push(src);
inq[src] = 1;
a[src] = INF;
d[src] = 0;
p[src] = 0;
while(!q.empty()) {
int x = q.front(); q.pop();
inq[x] = 0;
for(int i = 0; i < G[x].size(); i ++) {
Edge& e = edges[G[x][i]];
int y = e.to;
if(e.cap > e.flow && d[y] > d[x] + e.cost) {
d[y] = d[x] + e.cost;
a[y] = min(a[x], e.cap-e.flow);
p[y] = G[x][i];
if(!inq[y]) {
inq[y] = 1;
q.push(y);
}
}
}
}
if(d[targ] == INF) return false;
for(int u = targ; u != src; u = edges[p[u]].from) {
edges[p[u]].flow += a[targ];
edges[p[u]^1].flow -= a[targ];
}
flow += a[targ];
cost += (long long)d[targ]*(long long)a[targ];
return true;
}
void mincostMaxflow(int src, int targ, int& flow, long long& cost) {
flow = cost = 0;
while(bellmanFord(src, targ, flow, cost));
}
}mcmf;
int main() {
while(cin>>n && n) {
mcmf.init();
for(int from = 1; from <= n; from ++) {
int to, cost;
while(cin>>to && to) {
to += n;
cin>>cost;
mcmf.addEdge(from, to, 1, 0, cost);
}
}
int src = 2*n+1, targ = 2*n+2;
for(int i = 1; i <= n; i ++) {
mcmf.addEdge(src, i, 1, 0, 0);
mcmf.addEdge(i+n, targ, 1, 0, 0);
}
int flow;
long long cost;
mcmf.mincostMaxflow(src, targ, flow, cost);
if(flow == n)
printf("%lld\n",cost);
else
printf("N\n");
}
return 0;
}