poj 2516 Minimum Cost KM算法

来源:互联网 发布:怎么做钓鱼软件 编辑:程序博客网 时间:2024/04/30 13:59

题目链接:http://poj.org/problem?id=2516

之前用费用流做过一次,挺恶心的,各种数组,然后今天又用km做了一遍,更恶心了,不过还好学到了点东西

//km算法中左边点集要少于右边,经过拆点后的商店数一定是少于等于供货点的,所以商店为第一点集,供货点为第二点集#include <iostream>#include <cstdio>#include <cstring>#include <cctype>#include <vector>#include <queue>#include <algorithm>using namespace std;const int N = 100;const int INF = 0x3f3f3f3f;int n, m, k;int need1[N], need2[N];int dian[N][N], supply[N][N];int mpa[N][N][N];int s[N*10][N*10], match[N*10];int nx, ny;int lx[N*10], ly[N*10], slack[N*10];bool visx[N*10], visy[N*10];bool hungary(int v){    visx[v] = true;    for(int i = 0; i < ny; i++)    {        if(visy[i]) continue;        if(lx[v] + ly[i] == s[v][i])        {            visy[i] = true;            if(match[i] == -1 || hungary(match[i]))            {                match[i] = v;                return true;            }        }        else slack[i] = min(slack[i], lx[v] + ly[i] - s[v][i]);    }    return false;}void km(){    memset(ly, 0, sizeof ly);    for(int i = 0; i < nx; i++)        lx[i] = -INF;    for(int i = 0; i < nx; i++)        for(int j = 0; j < ny; j++)            lx[i] = max(lx[i], s[i][j]);    for(int i = 0; i < nx; i++)    {        memset(slack, 0x3f, sizeof slack);        while(true)        {            memset(visx, 0, sizeof visx);            memset(visy, 0, sizeof visy);            if(hungary(i)) break;            else            {                int tmp = INF;                for(int j = 0; j < ny; j++)                    if(! visy[j]) tmp = min(tmp, slack[j]);                for(int j = 0; j < nx; j++)                    if(visx[j]) lx[j] -= tmp;                for(int j = 0; j < ny; j++)                    if(visy[j]) ly[j] += tmp;                    else slack[j] -= tmp;            }        }    }}int main(){    while(scanf("%d%d%d", &n, &m, &k), n || m || k)    {        memset(need1, 0, sizeof need1);        memset(need2, 0, sizeof need2);        for(int i = 0; i < n; i++)            for(int j = 0; j < k; j++)            {                scanf("%d", &dian[i][j]);                need1[j] += dian[i][j];            }        for(int i = 0; i < m; i++)            for(int j = 0; j < k; j++)            {                scanf("%d", &supply[i][j]);                need2[j] += supply[i][j];            }        bool f = true;        for(int i = 0; i < k; i++)            if(need1[i] > need2[i])            {                f = false;                break;            }        for(int i = 0; i < k; i++)            for(int j = 0; j < n; j++)                for(int l = 0; l < m; l++)                    scanf("%d", &mpa[i][j][l]);        if(f == false)        {            printf("-1\n");            continue;        }        int res = 0;        for(int i = 0; i < k; i++)        {            memset(match, -1, sizeof match);            int tp1[N*10], tp2[N*10];            nx = 0, ny = 0;                        //拆点,因为km建图连边时无法表示数量,所以要把所有点拆成一个一个的,方式有点小技巧            for(int j = 0; j < n; j++)                for(int l = 0; l < dian[j][i]; l++)                    tp1[nx++] = j;            for(int j = 0; j < m; j++)                for(int l = 0; l < supply[j][i]; l++)                    tp2[ny++] = j;                                for(int j = 0; j < nx; j++)                for(int l = 0; l < ny; l++)                    s[j][l] = -mpa[i][tp1[j]][tp2[l]];            km();            for(int j = 0; j < ny; j++)                if(match[j] != -1)                    res += s[match[j]][j];        }        printf("%d\n", -res);    }    return 0;}
0 0
原创粉丝点击