KM算法( 一 )

A - 奔小康赚大钱
题意:
求解二分图的最优匹配

#include <iostream>
#include <cstring>
#include <cstdio>

using namespace std;
const int MAXN = 305;
const int INF = 0x3f3f3f3f;

int love[MAXN][MAXN];   // 记录每个妹子和每个男生的好感度
int ex_girl[MAXN];      // 每个妹子的期望值
int ex_boy[MAXN];       // 每个男生的期望值
bool vis_girl[MAXN];    // 记录每一轮匹配匹配过的女生
bool vis_boy[MAXN];     // 记录每一轮匹配匹配过的男生
int match[MAXN];        // 记录每个男生匹配到的妹子 如果没有则为-1
int slack[MAXN];        // 记录每个汉子如果能被妹子倾心最少还需要多少期望值

int N;


bool dfs(int girl)
{
    vis_girl[girl] = true;

    for (int boy = 0; boy < N; ++boy) {

        if (vis_boy[boy]) continue; // 每一轮匹配 每个男生只尝试一次

        int gap = ex_girl[girl] + ex_boy[boy] - love[girl][boy];

        if (gap == 0) {  // 如果符合要求
            vis_boy[boy] = true;
            if (match[boy] == -1 || dfs( match[boy] )) {    // 找到一个没有匹配的男生 或者该男生的妹子可以找到其他人
                match[boy] = girl;
                return true;
            }
        } else {
            slack[boy] = min(slack[boy], gap);  // slack 可以理解为该男生要得到女生的倾心 还需多少期望值 取最小值 备胎的样子【捂脸
        }
    }

    return false;
}

int KM()
{
    memset(match, -1, sizeof match);    // 初始每个男生都没有匹配的女生
    memset(ex_boy, 0, sizeof ex_boy);   // 初始每个男生的期望值为0

    // 每个女生的初始期望值是与她相连的男生最大的好感度
    for (int i = 0; i < N; ++i) {
        ex_girl[i] = love[i][0];
        for (int j = 1; j < N; ++j) {
            ex_girl[i] = max(ex_girl[i], love[i][j]);
        }
    }

    // 尝试为每一个女生解决归宿问题
    for (int i = 0; i < N; ++i) {

        fill(slack, slack + N, INF);    // 因为要取最小值 初始化为无穷大

        while (1) {
            // 为每个女生解决归宿问题的方法是 :如果找不到就降低期望值,直到找到为止

            // 记录每轮匹配中男生女生是否被尝试匹配过
            memset(vis_girl, false, sizeof vis_girl);
            memset(vis_boy, false, sizeof vis_boy);

            if (dfs(i)) break;  // 找到归宿 退出

            // 如果不能找到 就降低期望值
            // 最小可降低的期望值
            int d = INF;
            for (int j = 0; j < N; ++j)
                if (!vis_boy[j]) d = min(d, slack[j]);

            for (int j = 0; j < N; ++j) {
                // 所有访问过的女生降低期望值
                if (vis_girl[j]) ex_girl[j] -= d;

                // 所有访问过的男生增加期望值
                if (vis_boy[j]) ex_boy[j] += d;
                // 没有访问过的boy 因为girl们的期望值降低,距离得到女生倾心又进了一步!
                else slack[j] -= d;
            }
        }
    }

    // 匹配完成 求出所有配对的好感度的和
    int res = 0;
    for (int i = 0; i < N; ++i)
        res += love[ match[i] ][i];

    return res;
}
int main()
{
    while (~scanf("%d", &N)) {

        for (int i = 0; i < N; ++i)
            for (int j = 0; j < N; ++j)
                scanf("%d", &love[i][j]);

        printf("%d\n", KM());
    }
    return 0;
}

B - Going Home
题意:
给你一个N行M列的矩阵,其中“.”代表空地,“H”代表房子,“m”代表人,其中有n个房子和n个人。现在要求每个人进入一间房子,且人走一步需要支付1美元。
求最小需要花费多少美元才能让所有人都进入到房子中(每个人只能进入一间房子,每个房子只能容纳一个人)。
题解:
每个人都与每个房间连接一条边,边权为水平距离加竖直距离,然后得到一个二分图,对这个二分图运用KM算法.

#include <iostream>
#include <cstring>
#include <cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
const int MAXN = 210;
const int INF = 0x3f3f3f3f;
int love[MAXN][MAXN];   // 记录每个妹子和每个男生的好感度
int ex_girl[MAXN];      // 每个妹子的期望值
int ex_boy[MAXN];       // 每个男生的期望值
bool vis_girl[MAXN];    // 记录每一轮匹配匹配过的女生
bool vis_boy[MAXN];     // 记录每一轮匹配匹配过的男生
int match[MAXN];        // 记录每个男生匹配到的妹子 如果没有则为-1
int slack[MAXN];        // 记录每个汉子如果能被妹子倾心最少还需要多少期望值
int N;
struct Node
{
    int x,y;
};
Node xc[MAXN],yc[MAXN];
bool dfs(int girl)
{
    vis_girl[girl] = true;
    for (int boy = 0; boy < N; ++boy) {
        if (vis_boy[boy]) continue; // 每一轮匹配 每个男生只尝试一次
        int gap = ex_girl[girl] + ex_boy[boy] - love[girl][boy];
        if (gap == 0) {  // 如果符合要求
            vis_boy[boy] = true;
            if (match[boy] == -1 || dfs( match[boy] )) {    // 找到一个没有匹配的男生 或者该男生的妹子可以找到其他人
                match[boy] = girl;
                return true;
            }
        } else {
            slack[boy] = min(slack[boy], gap);  // slack 可以理解为该男生要得到女生的倾心 还需多少期望值 取最小值 备胎的样子【捂脸
        }
    }
    return false;
}
int KM()
{
    memset(match, -1, sizeof match);    // 初始每个男生都没有匹配的女生
    memset(ex_boy, 0, sizeof ex_boy);   // 初始每个男生的期望值为0
    // 每个女生的初始期望值是与她相连的男生最大的好感度
    for (int i = 0; i < N; ++i) {
        ex_girl[i] = love[i][0];
        for (int j = 1; j < N; ++j) {
            ex_girl[i] = max(ex_girl[i], love[i][j]);
        }
    }
    // 尝试为每一个女生解决归宿问题
    for (int i = 0; i < N; ++i) {
        fill(slack, slack + N, INF);    // 因为要取最小值 初始化为无穷大
        while (1) {
            // 为每个女生解决归宿问题的方法是 :如果找不到就降低期望值,直到找到为止
            // 记录每轮匹配中男生女生是否被尝试匹配过
            memset(vis_girl, false, sizeof vis_girl);
            memset(vis_boy, false, sizeof vis_boy);
            if (dfs(i)) break;  // 找到归宿 退出
            // 如果不能找到 就降低期望值
            // 最小可降低的期望值
            int d = INF;
            for (int j = 0; j < N; ++j)
                if (!vis_boy[j]) d = min(d, slack[j]);
            for (int j = 0; j < N; ++j) {
                // 所有访问过的女生降低期望值
                if (vis_girl[j]) ex_girl[j] -= d;

                // 所有访问过的男生增加期望值
                if (vis_boy[j]) ex_boy[j] += d;
                // 没有访问过的boy 因为girl们的期望值降低,距离得到女生倾心又进了一步!
                else slack[j] -= d;
            }
        }
    }
    // 匹配完成 求出所有配对的好感度的和
    int res = 0;
    for (int i = 0; i < N; ++i)
        res += love[ match[i] ][i];
    return res;
}
int main()
{
    int n,m,sum1,sum2;
    char c;
    while(scanf("%d%d",&n,&m)!=EOF,n+m)
    {
        sum1=0;
        sum2=0;
        getchar();
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<m;j++)
            {
                c=getchar();
                if(c=='m')
                {
                    xc[sum1].x=i;
                    xc[sum1++].y=j;
                }
                else if(c=='H')
                {
                    yc[sum2].x=i;
                    yc[sum2++].y=j;
                }
            }
            c=getchar();
        }
        for(int i=0;i<sum1;i++)
        {
            for(int j=0;j<sum2;j++)
            {
                love[i][j]=-abs(xc[i].x-yc[j].x)-abs(xc[i].y-yc[j].y);
            }
        }
        N=sum1;
        printf("%d\n",-KM());
    }
    return 0;
}

C - Interesting Housing Problem
题意:
现在有1所学校,N个学生,M个公寓,还有一个R值,代表学生对该公寓的满意度,如果为正数,越高表示越喜欢住在该所公寓,0表示不喜欢也不讨厌(意思就是可以住),如果为负数则代表不喜欢住,也不能住(要不小心起义~)。现在校长想让所有学生的满意度最高,而且学生跟公寓是一一对应的,另外,学生也不能入住那些没对公寓进行评价的。
求出最大值。
题解:

#include <iostream>
#include <cstring>
#include <cstdio>
#include<cmath>
#include<algorithm>

using namespace std;
const int MAXN = 510;
const int INF = 0x3f3f3f3f;

int love[MAXN][MAXN];   // 记录每个妹子和每个男生的好感度
int ex_girl[MAXN];      // 每个妹子的期望值
int ex_boy[MAXN];       // 每个男生的期望值
bool vis_girl[MAXN];    // 记录每一轮匹配匹配过的女生
bool vis_boy[MAXN];     // 记录每一轮匹配匹配过的男生
int match[MAXN];        // 记录每个男生匹配到的妹子 如果没有则为-1
int slack[MAXN];        // 记录每个汉子如果能被妹子倾心最少还需要多少期望值

int n,m;

struct Node
{
    int x,y;
};
Node xc[MAXN],yc[MAXN];
bool dfs(int girl)
{
    vis_girl[girl] = true;

    for (int boy = 0; boy < m; ++boy) {

        if (vis_boy[boy]) continue; // 每一轮匹配 每个男生只尝试一次

        int gap = ex_girl[girl] + ex_boy[boy] - love[girl][boy];

        if (gap == 0) {  // 如果符合要求
            vis_boy[boy] = true;
            if (match[boy] == -1 || dfs( match[boy] )) {    // 找到一个没有匹配的男生 或者该男生的妹子可以找到其他人
                match[boy] = girl;
                return true;
            }
        } else {
            slack[boy] = min(slack[boy], gap);  // slack 可以理解为该男生要得到女生的倾心 还需多少期望值 取最小值 备胎的样子【捂脸
        }
    }

    return false;
}

int KM()
{
    memset(match, -1, sizeof match);    // 初始每个男生都没有匹配的女生
    memset(ex_boy, 0, sizeof ex_boy);   // 初始每个男生的期望值为0

    // 每个女生的初始期望值是与她相连的男生最大的好感度
    for (int i = 0; i < n; ++i) {
        ex_girl[i] = love[i][0];
        for (int j = 1; j < m; ++j) {
            ex_girl[i] = max(ex_girl[i], love[i][j]);
        }
    }

    // 尝试为每一个女生解决归宿问题
    for (int i = 0; i < n; ++i) {

        fill(slack, slack + m, INF);    // 因为要取最小值 初始化为无穷大

        while (1) {
            // 为每个女生解决归宿问题的方法是 :如果找不到就降低期望值,直到找到为止

            // 记录每轮匹配中男生女生是否被尝试匹配过
            memset(vis_girl, false, sizeof vis_girl);
            memset(vis_boy, false, sizeof vis_boy);

            if (dfs(i)) break;  // 找到归宿 退出

            // 如果不能找到 就降低期望值
            // 最小可降低的期望值
            int d = INF;
            for (int j = 0; j < m; ++j)
                if (!vis_boy[j]) d = min(d, slack[j]);
            if(d==INF) return -1;  //无法松弛,找不到完备匹配
            for (int j = 0; j < n; ++j) {
                // 所有访问过的女生降低期望值
                if (vis_girl[j]) ex_girl[j] -= d;
            }

            for (int j = 0; j < m; ++j) {
                // 所有访问过的男生增加期望值
                if (vis_boy[j]) ex_boy[j] += d;
                // 没有访问过的boy 因为girl们的期望值降低,距离得到女生倾心又进了一步!
                else slack[j] -= d;
            }
        }
    }

    // 防止匹配到不存在的边
    int res = 0,flag=0;
    for(int i = 0; i < m; i++){
        if(match[i]==-1||love[match[i]][i]==-INF)
            continue;
        res += love[match[i]][i];
        flag++;
    }
    if(flag<n) res=-1;
    return res;
}
int main()
{
    int a,b,c,e,cas=1;
    while(scanf("%d%d%d",&n,&m,&e)!=EOF){
        for(int i=0;i<n;i++)
         for(int j=0;j<m;j++)
             love[i][j]=-INF;
        for(int i=0;i<e;i++) {
            scanf("%d%d%d",&a,&b,&c);
            if(c<0)  continue;
            love[a][b]=c;
        }
        printf("Case %d: %d\n",cas++,KM());
    }
    return 0;
}

D - Special Fish
题意:
武大荷塘有N条鱼(不分性别),每条鱼有一个价值vi.且给出一个N*N的矩阵,矩阵中(i,j)格为1表示,第i条鱼会攻击第j条鱼并产下卵.
产卵的数量= vi XOR vj. 现在每条鱼只能被攻击1次(一条鱼只能攻击1次且被攻击1次),且每条鱼只会攻击它可能会攻击的所有鱼中的一条(哪些其他鱼它会攻击已经在N*N矩阵中给出).现在要你求这N条鱼产卵数目的最大值.
题解:

#include <iostream>
#include <cstring>
#include <cstdio>
#include<cmath>
#include<algorithm>

using namespace std;
const int MAXN = 110;
const int INF = 0x3f3f3f3f;

int love[MAXN][MAXN];   // 记录每个妹子和每个男生的好感度
int ex_girl[MAXN];      // 每个妹子的期望值
int ex_boy[MAXN];       // 每个男生的期望值
bool vis_girl[MAXN];    // 记录每一轮匹配匹配过的女生
bool vis_boy[MAXN];     // 记录每一轮匹配匹配过的男生
int match[MAXN];        // 记录每个男生匹配到的妹子 如果没有则为-1
int slack[MAXN];        // 记录每个汉子如果能被妹子倾心最少还需要多少期望值

int val[MAXN];
int N;

struct Node
{
    int x,y;
};
Node xc[MAXN],yc[MAXN];
bool dfs(int girl)
{
    vis_girl[girl] = true;

    for (int boy = 0; boy < N; ++boy) {

        if (vis_boy[boy]) continue; // 每一轮匹配 每个男生只尝试一次

        int gap = ex_girl[girl] + ex_boy[boy] - love[girl][boy];

        if (gap == 0) {  // 如果符合要求
            vis_boy[boy] = true;
            if (match[boy] == -1 || dfs( match[boy] )) {    // 找到一个没有匹配的男生 或者该男生的妹子可以找到其他人
                match[boy] = girl;
                return true;
            }
        } else {
            slack[boy] = min(slack[boy], gap);  // slack 可以理解为该男生要得到女生的倾心 还需多少期望值 取最小值 备胎的样子【捂脸
        }
    }

    return false;
}

int KM()
{
    memset(match, -1, sizeof match);    // 初始每个男生都没有匹配的女生
    memset(ex_boy, 0, sizeof ex_boy);   // 初始每个男生的期望值为0

    // 每个女生的初始期望值是与她相连的男生最大的好感度
    for (int i = 0; i < N; ++i) {
        ex_girl[i] = love[i][0];
        for (int j = 1; j < N; ++j) {
            ex_girl[i] = max(ex_girl[i], love[i][j]);
        }
    }

    // 尝试为每一个女生解决归宿问题
    for (int i = 0; i < N; ++i) {

        fill(slack, slack + N, INF);    // 因为要取最小值 初始化为无穷大

        while (1) {
            // 为每个女生解决归宿问题的方法是 :如果找不到就降低期望值,直到找到为止

            // 记录每轮匹配中男生女生是否被尝试匹配过
            memset(vis_girl, false, sizeof vis_girl);
            memset(vis_boy, false, sizeof vis_boy);

            if (dfs(i)) break;  // 找到归宿 退出

            // 如果不能找到 就降低期望值
            // 最小可降低的期望值
            int d = INF;
            for (int j = 0; j < N; ++j)
                if (!vis_boy[j]) d = min(d, slack[j]);

            for (int j = 0; j < N; ++j) {
                // 所有访问过的女生降低期望值
                if (vis_girl[j]) ex_girl[j] -= d;

                // 所有访问过的男生增加期望值
                if (vis_boy[j]) ex_boy[j] += d;
                // 没有访问过的boy 因为girl们的期望值降低,距离得到女生倾心又进了一步!
                else slack[j] -= d;
            }
        }
    }

    // 匹配完成 求出所有配对的好感度的和
    int res = 0;
    for (int i = 0; i < N; ++i)
        res += love[ match[i] ][i];

    return res;
}
int main()
{
    while(scanf("%d",&N)!=EOF,N)
    {
        for(int i=0;i<N;i++)
            scanf("%d",val+i);
        memset(love,0,sizeof(love));
        char c;
        for(int i=0;i<N;i++)
        {
            for(int j=0;j<N;j++)
            {
                scanf(" %c",&c);
                if(c=='1')
                {
                    love[i][j]=val[i]^val[j];
                }
            }
        }
        printf("%d\n",KM());
    }

    return 0;
}

S - Ants
题意:
跟B题差不多,给定n个虫的坐标和m颗树的坐标(m>n),n个从要搬迁到树中,每棵树只能住一个虫。求出搬迁的最短距离。
题解:
两点距离直接算就可以了,虫树两两之间连边,权值可能是小数,所以要控制浮点误差eps

#include<cstdio>
#include<algorithm>
#include<queue>
#include<cmath>
#include<cstring>
using namespace std;
const int MAXN=110;
const int INF=0x3f3f3f3f;
double love[MAXN][MAXN],slack[MAXN];
int match[MAXN],ans[MAXN];
int visb[MAXN],visg[MAXN];
double eb[MAXN],eg[MAXN];
int n;
double eps=1e-10;
bool DFS(int girl)
{
    visg[girl]=true;
    for(int boy=1;boy<=n;boy++)
    {
        if(visb[boy]) continue;
        double gap=abs(eb[boy]+eg[girl]-love[girl][boy]);
        if(gap<=eps)
        {
            visb[boy]=1;
            if(match[boy]==-1||DFS(match[boy]))
            {
                match[boy]=girl;
                return true;
            }
        }
        else slack[boy]=min(slack[boy],gap);
    }
    return false;
}
void km()
{
    memset(match,-1,sizeof(match));
    memset(eb,0,sizeof(eb));
    for(int i=1;i<=n;i++)
    {
        eg[i]=love[i][1];
        for(int j=2;j<=n;j++)
        {
            eg[i]=max(eg[i],love[i][j]);
        }
    }
    for(int i=1;i<=n;i++)
    {
        fill(slack+1,slack+1+n,INF);
        while(true)
        {
            memset(visg,0,sizeof(visg));
            memset(visb,0,sizeof(visb));
            if(DFS(i)) break;
            double d=INF;
            for(int i=1;i<=n;i++)
            {
                if(!visb[i]) d=min(d,slack[i]);
            }
            for(int i=1;i<=n;i++)
            {
                if(visg[i]) eg[i]-=d;
                if(visb[i]) eb[i]+=d;
                else slack[i]-=d;
            }
        }
    }
    for(int i=1;i<=n;i++)
    {
        printf("%d\n",match[i]);
    }
}
double dist(pair<double,double> p1,pair<double,double> p2)
{
    return -sqrt((p1.first-p2.first)*(p1.first-p2.first)+(p1.second-p2.second)*(p1.second-p2.second));
}
pair<double,double>  worm[MAXN];
pair<double,double> tree[MAXN];
int main()
{
    while(scanf("%d",&n)!=EOF)
    {
        for(int i=1;i<=n;i++)
        {
            scanf("%lf%lf",&worm[i].first,&worm[i].second);
        }
        for(int i=1;i<=n;i++)
        {
            scanf("%lf%lf",&tree[i].first,&tree[i].second);
        }
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
            {
                love[i][j]=dist(tree[i],worm[j]);
            }
        }
        km();
    }
}

E - Chocolate
题意:
有n个盒子组成一个圆,盒子总共有不超过n个蛋糕,有的有多于1个,有的为0。可以将一个盒子里的蛋糕往左右两个盒子里移动,一次只能移动一个,使最终每个盒子里有不超过一个蛋糕(可以没有),求最小的移动数
题解:
要使得每个盒子的蛋糕最多为一个,那么那些多于一个蛋糕的蛋糕盒里的蛋糕只能移向没有蛋糕的蛋糕盒;也就是多出的蛋糕和空蛋糕盒构成二分图,他们的距离为蛋糕盒之间的距离,由于是排列是圆形的,所以考虑另一个方向的距离

    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include<cmath>
    #include<algorithm>

    using namespace std;
    const int MAXN = 510;
    const int INF = 0x3f3f3f3f;

    int love[MAXN][MAXN];
    int ex_girl[MAXN];
    int ex_boy[MAXN];
    bool vis_girl[MAXN];
    bool vis_boy[MAXN];
    int match[MAXN];
    int slack[MAXN];

    int N,M;

    bool dfs(int girl)
    {
        vis_girl[girl] = true;

        for (int boy = 0; boy < M; ++boy) {

            if (vis_boy[boy]) continue;

            int gap = ex_girl[girl] + ex_boy[boy] - love[girl][boy];

            if (gap == 0) {
                vis_boy[boy] = true;
                if (match[boy] == -1 || dfs( match[boy] )) {
                    match[boy] = girl;
                    return true;
                }
            } else {
                slack[boy] = min(slack[boy], gap);
            }
        }

        return false;
    }

    int KM()
    {
        memset(match, -1, sizeof match);
        memset(ex_boy, 0, sizeof ex_boy);

        for (int i = 0; i < N; ++i) {
            ex_girl[i] = love[i][0];
            for (int j = 1; j < M; ++j) {
                ex_girl[i] = max(ex_girl[i], love[i][j]);
            }
        }


        for (int i = 0; i < N; ++i) {

            fill(slack, slack + M, INF);

            while (1) {
                memset(vis_girl, false, sizeof vis_girl);
                memset(vis_boy, false, sizeof vis_boy);

                if (dfs(i)) break;

                int d = INF;
                for (int j = 0; j < M; ++j)
                    if (!vis_boy[j]) d = min(d, slack[j]);

                for (int j = 0; j < N; ++j) {
                    if (vis_girl[j]) ex_girl[j] -= d;
                }
                for(int j=0;j<M;j++)
                {
                    if(vis_boy[j]) ex_boy[j]+=d;
                    else slack[j]-=d;
                }
            }
        }
        int res = 0;
        for (int i = 0; i < M; ++i)
        {
            if(match[i]==-1||love[match[i]][i]==-INF) continue;
            res += love[ match[i] ][i];
        }
        return res;
    }
    int main()
    {
        while(scanf("%d",&N)!=EOF)
        {
            if(N==0) break;
            int val[MAXN];
            for(int i=0;i<N;i++)
            {
                scanf("%d",&val[i]);
            }
            int index[MAXN];
            int box=0;
            for(int i=0;i<N;i++)
            {
                if(!val[i]) index[box++]=i;
            }
            int cnt=0;
            for(int i=0;i<N;i++)
            {
                for(int j=0;j<N;j++)
                {
                    love[i][j]=-INF;
                }
            }
            for(int i=0;i<N;i++)
            {
                if(val[i]>1)
                {
                    for(int j=1;j<val[i];j++)
                    {
                        for(int k=0;k<box;k++)
                        {
                            love[cnt][k]=-min(abs(index[k]-i),N-abs(index[k]-i));
                        }
                        cnt++;
                    }
                }
            }
            N=cnt;
            M=box;
            printf("%d\n",-KM());
        }
        return 0;
    }

F - One fihgt one
题意;
吕布和曹操对战,给出两队的对战情况,要求吕布伤害最小,即求最小权匹配
题解:
这里用到了map<string,int>,第一次知道插入map的key不单单是用string,还可以是char数组

    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include<cmath>
    #include<algorithm>
    #include<string>
    #include<map>
    using namespace std;
    const int MAXN = 205;
    const int INF = 0x3f3f3f3f;
    int love[MAXN][MAXN];
    int ex_girl[MAXN];
    int ex_boy[MAXN];
    bool vis_girl[MAXN];
    bool vis_boy[MAXN];
    int match[MAXN];
    int slack[MAXN];
    int N,M;
    bool dfs(int girl)
    {
        vis_girl[girl] = true;
        for (int boy = 0; boy < M; ++boy) {
            if (vis_boy[boy]) continue;
            int gap = ex_girl[girl] + ex_boy[boy] - love[girl][boy];
            if (gap == 0) {
                vis_boy[boy] = true;
                if (match[boy] == -1 || dfs( match[boy] )) {
                    match[boy] = girl;
                    return true;
                }
            } else {
                slack[boy] = min(slack[boy], gap);
            }
        }
        return false;
    }
    int KM()
    {
        memset(match, -1, sizeof match);
        memset(ex_boy, 0, sizeof ex_boy);
        for (int i = 0; i < N; ++i) {
            ex_girl[i] = love[i][0];
            for (int j = 1; j < M; ++j) {
                ex_girl[i] = max(ex_girl[i], love[i][j]);
            }
        }
        for (int i = 0; i < N; ++i) {
            fill(slack, slack + M, INF);
             while (1) {
                memset(vis_girl, false, sizeof vis_girl);
                memset(vis_boy, false, sizeof vis_boy);
                if (dfs(i)) break;
                int d = INF;
                for (int j = 0; j < M; ++j)
                    if (!vis_boy[j]) d = min(d, slack[j]);

                for (int j = 0; j < N; ++j) {
                    if (vis_girl[j]) ex_girl[j] -= d;
                }
                for(int j=0;j<M;j++)
                {
                    if(vis_boy[j]) ex_boy[j]+=d;
                    else slack[j]-=d;
                }
            }
        }
        int res = 0;
        for (int i = 0; i < M; ++i)
        {
            if(match[i]==-1||love[match[i]][i]==-INF) continue;
            res += love[ match[i] ][i];
        }
        return res;
    }
    int main()
    {
        int T;
        int hurt;
        int index1,index2;
        char name1[25];
        char name2[25];
        int x,y;
        while(scanf("%d%d%d",&N,&M,&T)!=EOF)
        {
            map<string,int> lv,cao;
            index1=0;
            index2=0;
            for(int i=0;i<N;i++)
            {
                for(int j=0;j<M;j++)
                {
                    love[i][j]=-INF;
                }
            }
            while(T--)
            {
                scanf("%s%s%d",name1,name2,&hurt);
                map<string,int>::iterator it;
                it=lv.find(name1);
                if(it==lv.end())
                {
                    x=index1;
                    lv[name1]=index1++;
                }
                else x=it->second;
                it=cao.find(name2);
                if(it==cao.end())
                {
                    y=index2;
                    cao[name2]=index2++;
                }
                else y=it->second;
                love[x][y]=-hurt;
            }
            printf("%d\n",-KM());
        }
        return 0;
    }

P - Going Home
题意:
这题和B题一样的,代码就不贴了
Q - Supervisor, Supervisee
题意:
n个雇员和n个雇主,给出n个雇员对每个雇主的评价(越低分越满意),同样给出n个雇主对每个雇员的评价(越低分越满),求出匹配方案使得总满意度最高(分数最低)。如果存在多种方案,输出所有的方案。
题解: ( 双匹配边 + 输出所有合理方案 )
这题出现了双边,即匹配的双方都有各自的看法,但是其实还是一样的,只要建图两次,两次的满意度加在一起就可以了,然后求最小权匹配。
至于输出多种方案:因为0 < N < 15,N比较小,我们先求出最小权匹配的ans,然后再对雇员进行全排列,每个雇主与之匹配,检测匹配值是否等于ans,等于再输出相应的方案。注意:dfs进行全排列的时候,需要剪枝:如果当前积分比ans还要小(我套的是边取负值,最大权匹配的模板),那就不要继续走下去了,最后的方案一定不是对于ans的

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN=20;
const int INF=0x3f3f3f3f;
int visb[MAXN],visg[MAXN];
int eboy[MAXN],egirl[MAXN];
int match[MAXN],slack[MAXN];
int love[MAXN][MAXN],n,res;
bool DFS(int girl)
{
    visg[girl]=1;
    for(int i=1;i<=n;i++)
    {
        if(visb[i]) continue;
        int gap=eboy[i]+egirl[girl]-love[girl][i];
        if(gap==0)
        {
            visb[i]=1;
            if(match[i]==-1||DFS(match[i]))
            {
                match[i]=girl;
                return true;
            }
        }
        else slack[i]=min(slack[i],gap);
    }
    return false;
}
void km()
{
    memset(eboy,0,sizeof(eboy));
    memset(match,-1,sizeof(match));
    for(int i=1;i<=n;i++)
    {
        egirl[i]=love[i][1];
        for(int j=2;j<=n;j++)
        {
            egirl[i]=max(egirl[i],love[i][j]);
        }
    }
    for(int i=1;i<=n;i++)
    {
        memset(slack,INF,sizeof(slack));
        while(true)
        {
            memset(visb,0,sizeof(visb));
            memset(visg,0,sizeof(visg));
            if(DFS(i)) break;
            int d=INF;
            for(int j=1;j<=n;j++)
            {
                if(!visb[j]) d=min(d,slack[j]);
            }
            for(int j=1;j<=n;j++)
            {
                if(visg[j]) egirl[j]-=d;

                if(visb[j]) eboy[j]+=d;
                else slack[j]-=d;
            }
        }
    }
    res=0;
    for(int i=1;i<=n;i++)
    {
        res+=love[match[i]][i];
    }
}
int num[MAXN],vis[MAXN],cnt,sum;
void km_dfs(int dept)//全排列
{
    if(sum<res) return;//剪枝
    if(dept>n)
    {
        if(sum!=res) return;
        printf("Best Pairing %d\n",++cnt);
        for(int i=1;i<=n;i++)
        {
            printf("Supervisor %d with Employee %d\n",i,num[i]);
        }
        return;
    }
    for(int i=1;i<=n;i++)
    {
        if(vis[i]) continue;
        vis[i]=1;
        num[dept]=i;
        sum+=love[dept][i];
        km_dfs(dept+1);
        vis[i]=0;//回溯
        sum-=love[dept][i];//回溯
    }
}
int main()
{
    int t,x;
    scanf("%d",&t);
    for(int cas=1;cas<=t;cas++)
    {
        scanf("%d",&n);
        memset(love,0,sizeof(love));
        for(int i=1;i<=n;i++)
        {
            for(int j=0;j<n;j++)
            {
                scanf("%d",&x);
                love[x][i]-=j;
            }
        }

        for(int i=1;i<=n;i++)
        {
            for(int j=0;j<n;j++)
            {
                scanf("%d",&x);
                love[i][x]-=j;
            }
        }
        km();
        cnt=sum=0;
        memset(vis,0,sizeof(vis));
        //平均满意度:因为有2*n个点,所以除以2*n
        printf("Data Set %d, Best average difference: %.6lf\n",cas,0.5*(-res)/n);
        km_dfs(1);
        printf("\n");
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 196,264评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,549评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,389评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,616评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,461评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,351评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,776评论 3 387
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,414评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,722评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,760评论 2 314
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,537评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,381评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,787评论 3 300
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,030评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,304评论 1 252
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,734评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,943评论 2 336

推荐阅读更多精彩内容

  • G - Cyclic Tour题意:图中有n个点和m条有向边现在要将该图分成若干环,每个环中至少有两个点。环与环不...
    Gitfan阅读 772评论 0 1
  • 不错的博客求next数组: 注意:求next数组得到的最长公共前缀后缀是可以重叠的:比如:ababa,next[5...
    Gitfan阅读 833评论 0 0
  • 背景 一年多以前我在知乎上答了有关LeetCode的问题, 分享了一些自己做题目的经验。 张土汪:刷leetcod...
    土汪阅读 12,713评论 0 33
  • (一)巴什博弈只有一堆n个物品,两个人轮流从这堆物品中取物,规定每次至少取一个,最多取m个。最后取光者得胜。显然,...
    Gitfan阅读 899评论 0 0
  • contentSize 是scrollview可以滚动的区域,比如frame = (0 ,0 ,320 ,480)...
    chenhh6701阅读 1,729评论 0 3