这道题是uva 1349 的简化版,那题没过,不知道为什么。我觉得那题就是多了一个先判断他最大匹配数是不是n,是的话,再找最优匹配。
回到这题,匹配问题,又是有向图,直接想到了拆点法。然后发现若每个点都恰好属于一个环,即是每个点的初度入度都为1,所以只要这个二分图有完美匹配,就是满足题目意思的。
这样的话就是找最小匹配了,处理方法,每条边的距离用一个INF值减,最后累加时别忘了再用INF减回来。
1 6 9 1 2 5 2 3 5 3 1 10 3 4 12 4 1 8 4 6 11 5 4 7 5 6 9 6 5 4
42
#include <cstdio> #include <cstring> #include <vector> #include <algorithm> using namespace std; const int maxn = 200 + 5; // 顶点的最大数目 const int INF = 1000000000; // 最大权匹配 struct KM { int n; // 左右顶点个数 vector<int> G[maxn]; // 邻接表 int W[maxn][maxn]; // 权值 int Lx[maxn], Ly[maxn]; // 顶标 int left[maxn]; // left[i]为右边第i个点的匹配点编号,-1表示不存在 bool S[maxn], T[maxn]; // S[i]和T[i]为左/右第i个点是否已标记 void init(int n) { this->n = n; for(int i = 0; i < n; i++) G[i].clear(); memset(W, 0, sizeof(W)); } void AddEdge(int u, int v, int w) { G[u].push_back(v); W[u][v] = w; } bool match(int u){ S[u] = true; for(int i = 0; i < G[u].size(); i++) { int v = G[u][i]; if (Lx[u]+Ly[v] == W[u][v] && !T[v]){ T[v] = true; if (left[v] == -1 || match(left[v])){ left[v] = u; return true; } } } return false; } void update(){ int a = INF; for(int u = 0; u < n; u++) if(S[u]) for(int i = 0; i < G[u].size(); i++) { int v = G[u][i]; if(!T[v]) a = min(a, Lx[u]+Ly[v] - W[u][v]); } for(int i = 0; i < n; i++) { if(S[i]) Lx[i] -= a; if(T[i]) Ly[i] += a; } } void solve() { for(int i = 0; i < n; i++) { Lx[i] = *max_element(W[i], W[i]+n); left[i] = -1; Ly[i] = 0; } for(int u = 0; u < n; u++) { for(;;) { for(int i = 0; i < n; i++) S[i] = T[i] = false; if(match(u)) break;else update(); } }printf("END\n"); } }; KM solver; int main(){ int t,n,m; scanf("%d",&t); while(t--){ scanf("%d%d",&n,&m); solver.init(n); while(m--){ int from,to,dist; scanf("%d%d%d",&from,&to,&dist);from--;to--; if(solver.W[from][to] != 0){ dist = max(INF-dist,solver.W[from][to]); } else dist = INF - dist; solver.AddEdge(from,to,dist); } solver.solve(); int ans = 0; for(int i = 0;i < n;i++) ans += INF-solver.W[solver.left[i]][i]; printf("%d\n",ans); } return 0; }