ZOJ 2342 Roads 二分图匹配

来源:互联网 发布:全职法师实体书淘宝 编辑:程序博客网 时间:2024/06/06 06:34

题意:给你一个n个点m条边的带权无向图 其前n-1条边构成一个生成树 问你修改各条边的值 使得前n-1条边构成最小生成树 并且各条边的改变量之和最小

思路:既然是要求前n-1条边组成最小生成树 则这前n-1条边权值一定不能增大 而剩余的边权值一定不能减少 对于前n-1条边权值w[a]和剩下的边w[b] 使得 w[a]-d[a]<w[b]+d[b] 移项得 w[a]-w[b]<d[a]+d[b] 着看起来是不是有点像KM算法里的可行顶标 这里也是本题的精髓 对于前n-1条边 建立一棵生成树 后每加一条边 就会产生一条回路 为了使得新加入的边的权值在所在的圈中最大 就要对圈中所有w[a]>w[b]的边 改变其权值w[a]-d[a]<w[b]+d[b] 最后直接套KM即可

#include <cstdio>#include <cstring>#include <algorithm>#include <vector>using namespace std;const int maxn = 400 + 10;int cas = 0;struct Edge{          int v, id, d, next;          Edge(int v, int id, int d, int next) : v(v), id(id), d(d), next(next) {}          Edge() {}};struct WORK{          int w[maxn][maxn], n;          int Lx[maxn], Ly[maxn];          int left[maxn];          int cntE, Head[maxn];          int c[maxn*2];          bool S[maxn], T[maxn], vis[maxn];          int N, M;          Edge e[maxn*2];          void init(int n){                    this -> n = n;                    cntE = 0;                    memset(Head, -1, sizeof(Head));          }          void Add(int u, int v, int id, int d){                    e[cntE] = Edge(v, id, d, Head[u]);                    Head[u] = cntE++;                    e[cntE] = Edge(u, id, d, Head[v]);                    Head[v] = cntE++;          }          bool match(int i){                    S[i] = true;                    for(int j = 1; j <= n; j++)if(Lx[i] + Ly[j] == w[i][j] && !T[j]){                              T[j] = true;                              if(!left[j] || match(left[j])){                                        left[j] = i;                                        return true;                              }                    }                    return false;          }          void update(){                    int a = 1 << 30;                    for(int i = 1; i <= n; i++)if(S[i])                              for(int j = 1; j <= n; j++)if(!T[j])                              a = min(a, Lx[i] + Ly[j] - w[i][j]);                    for(int i = 1; i <= n; i++){                              if(S[i]) Lx[i] -= a;                              if(T[i]) Ly[i] += a;                    }          }          void KM(){                    for(int i = 1; i <= n; i++){                              left[i] = Lx[i] = Ly[i] = 0;                              for(int j = 1; j <= n; j++) Lx[i] = max(Lx[i], w[i][j]);                    }                    for(int i = 1; i <= n; i++){                              for(;;){                                        for(int j = 1; j <= n; j++) S[j] = T[j] = 0;                                        if(match(i)) break;                                        else update();                              }                    }          }          bool build(int s, int t, int id, int d){                    if(s == t) return true;                    vis[s] = true;                    for(int i = Head[s]; ~i; i = e[i].next){                              if(vis[e[i].v]) continue;                              if(build(e[i].v, t, id, d)){                                        if(e[i].d > d) w[e[i].id][id] = e[i].d - d;                                        return true;                              }                    }                    return false;          }          void solve(){                    scanf("%d%d", &N, &M);                    init( max(N - 1, M - N + 1) );                    memset(w, 0, sizeof(w));                    int u, v, d;                    for(int i = 1; i < N; i++){                              scanf("%d%d%d", &u, &v, &d);                              c[i] = d;                              Add(u, v, i, d);                    }                    for(int i = N; i <= M; i++){                              scanf("%d%d%d", &u, &v, &d);                              c[i] = d;                              memset(vis, false, sizeof(vis));                              build(u, v, i - N + 1, d);                    }                    KM();                    output();          }          void output(){                    if(cas++) printf("\n");                    for(int i = 1; i <= M; i++){                              if(i < N) printf("%d\n", c[i] - Lx[i]);                              else printf("%d\n", c[i] + Ly[i-N+1]);                    }          }}solver;int main(){          //freopen("in.txt", "r", stdin);          int T;          scanf("%d", &T);          while(T--) solver.solve();          return 0;}


0 0
原创粉丝点击