HDU 1853 Cyclic Tour KM算法

来源:互联网 发布:fgo完整数据取消 编辑:程序博客网 时间:2024/05/29 18:55

此题的模型转化比较好


题目说是有向图,把图分成一些环,使得构成这些环总的边权值最小, 环的特性是最少两个点。

观察环这个限制,实际上就是每个点有且只有一个出边,有且只有一个入边,并且不能是自环

这可以跟匹配联系起来,将每个点拆成u, u' 然后 如果有一条边(u,v, w)就建一条(u, v ', w)的边

最后求匹配,如果左边的点都匹配到了,显然是每个点都有了一个出边,右边的点都匹配到后就是每个点都有了一个入边

这一点其实跟以前学计数那个循环有点相似。 

#include <iostream>#include <algorithm>#include <cstring>#include <string>#include <cstdio>#include <cmath>#include <queue>#include <map>#include <set>#define eps 1e-5#define MAXN 333#define MAXM 333#define INF 100000007using namespace std;int n, m, ny, nx;int w[MAXN][MAXM];int lx[MAXN], ly[MAXM];int linky[MAXM];int visx[MAXN], visy[MAXM];int slack[MAXM];bool find(int x){    visx[x] = 1;    for(int y = 1; y <= ny; y++)    {        if(visy[y]) continue;        int t = lx[x] + ly[y] - w[x][y];        if(t == 0)        {            visy[y] = 1;            if(linky[y] == -1 || find(linky[y]))            {                linky[y] = x;                return true;            }        }         else if(slack[y] > t) slack[y] = t;    }    return false;}int KM(){    memset(linky, -1, sizeof(linky));    for(int i = 1; i <= nx; i++) lx[i] = -INF;    memset(ly, 0, sizeof(ly));    for(int i = 1; i <= nx; i++)        for(int j = 1; j <= ny; j++)            if(w[i][j] > lx[i]) lx[i] = w[i][j];    for(int x = 1; x <= nx; x++)    {        for(int i = 1; i <= ny; i++) slack[i] = INF;        while(true)        {            memset(visx, 0, sizeof(visx));            memset(visy, 0, sizeof(visy));            if(find(x)) break;            int d = INF;            for(int i = 1; i <= ny; i++)                if(!visy[i]) d = min(d, slack[i]);            if(d == INF) return -1;            for(int i = 1; i <= nx; i++)                if(visx[i]) lx[i] -=d;            for(int i = 1; i <= ny; i++)                if(visy[i]) ly[i] += d;                    else slack[i] -= d;        }    }    int tp = 0, cnt = 0;    for(int i = 1; i <= ny; i++)        if(linky[i] != -1 && w[linky[i]][i] != -INF)        {            tp += w[linky[i]][i];            cnt++;        }    if(cnt != nx) return -1;    return -tp;}int main(){    while(scanf("%d%d", &n, &m) != EOF)    {        int x, y, z;        for(int i = 1; i <= n; i++)            for(int j = 1; j <= n; j++)                w[i][j] = -INF;        nx = ny = n;        for(int i = 1; i <= m; i++)        {            scanf("%d%d%d", &x, &y, &z);            if(-z > w[x][y]) w[x][y] = -z;        }        printf("%d\n", KM());    }    return 0;}