ZOJ-3933-Team Formation【二分图最佳匹配】【KM】


3933-Team Formation


For an upcoming programming contest, Edward, the headmaster of Marjar University, is forming several two-man teams from students of his university.

There are n students in the university numbered from 1 to n. There are two groups of students in the university and each student belongs to exactly one of the groups. For each student in the university, he/she has a list of students whom he/she will never form a team with. For two students a and b, if b is in a’s list, a is also in b’s list.

Edward will only form high-performance teams. A high-performance team is a team which consists of two students from different groups who are willing to form a team. In addition, Edward wants as many as possible girls to participate in the contest. You, as Edward’s secretary, need to form as many high-performance teams as possible and at the same time maximize the number of girls in the teams.

Input
There are multiple test cases. The first line of input contains an integer T, indicating the number of test cases. For each test case:

The first line contains an integer n (1 ≤ n ≤ 500) – the number of students.

The second line contains a binary string of length n. If the i-th character is 0 then i-th student is in the first group, otherwise the student is in the second group.

The third line contains a binary string of length n. If the i-th character is 0 then i-th student is a girl, otherwise the student is a boy.

In the following n lines, each line starts with an integer m followed with m distinct integers, denoting the list of i-th student.

Output
For each case, output two integers a and b denoting the number of high-performance teams and the number of girls.

In the next a lines, each contains two integers xi and yi denoting the students in i-th team.

If there are multiple solutions, you can output any of them.

Sample Input
1
3
101
000
0
0
0
Sample Output
1 2
1 2

题目链接:ZOJ-3933

题目大意:有n个人,每个人属于0组或者1组

现在想要组队,每个队伍必须是一个0组的人和一个1组的组队

问题:在组队数最多的情况下,队伍中女生数量最多能有多少个?

题目思路:比较直白的带权匹配问题,可以用KM算法。小技巧,将女生权值设置为10001,男生权值设置为10000,这样利用最佳匹配,得到的答案肯定是优先边多,其次是权值大,女生权值影响小。

比赛的时候想到了做法,可是看题目数据感觉n^3的模板会超时没敢写,结果真的就是这么写。。。

以下是代码:

//
//  ZOJ-3933-Team Formation.cpp
//  ZOJ
//
//  Created by pro on 16/4/13.
//  Copyright (c) 2016年 pro. All rights reserved.
//

#include <vector>
#include <map>
#include <set>
#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <string>
#include <cstring>
using namespace std;
/* KM算法
 * 复杂度O(nx*nx*ny)
 * 求最大权匹配
 * 若求最小权匹配,可将权值取相反数,结果取相反数
 * 点的编号从0开始
 */
const int N = 505;
const int INF = 0x3f3f3f3f;
int nx,ny;//两边的点数 nx,第一队的个数,ny,第二队的个数。注意 nx <= ny,否则会死循环。
int g[N][N];//二分图描述  g[i][j],i属于第一队,j属于第二队。
int linker[N],lx[N],ly[N];//y中各点匹配状态,x,y中的点标号
int slack[N];
bool visx[N],visy[N];
bool DFS(int x)
{
    visx[x] = true;
    for(int y = 0; y < ny; y++)
    {
        if(visy[y])continue;
        int tmp = lx[x] + ly[y] - g[x][y];
        if(tmp == 0)
        {
            visy[y] = true;
            if(linker[y] == -1 || DFS(linker[y]))
            {
                linker[y] = x;
                return true;
            }
        }
        else if(slack[y] > tmp)
            slack[y] = tmp;
    }
    return false;
}
int KM()
{
    memset(linker,-1,sizeof(linker));
    memset(ly,0,sizeof(ly));
    for(int i = 0;i < nx;i++)
    {
        lx[i] = -INF;
        for(int j = 0;j < ny;j++)
            if(g[i][j] > lx[i])
                lx[i] = g[i][j];
    }
    for(int x = 0;x < nx;x++)
    {
        for(int i = 0;i < ny;i++)
            slack[i] = INF;
        while(true)
        {
            memset(visx,false,sizeof(visx));
            memset(visy,false,sizeof(visy));
            if(DFS(x))break;
            int d = INF;
            for(int i = 0;i < ny;i++)
                if(!visy[i] && d > slack[i])
                    d = slack[i];
            for(int i = 0;i < nx;i++)
                if(visx[i])
                    lx[i] -= d;
            for(int i = 0;i < ny;i++)
            {
                if(visy[i])ly[i] += d;
                else slack[i] -= d;
            }
        }
    }
    int res = 0;
    for(int i = 0;i < ny;i++)
        if(linker[i] != -1)
            res += g[linker[i]][i];
    return res;
}
//以上都是模板部分
int vis[505][505];
int main()
{
    int t,n,a,m;
    cin >> t;
    while(t--)
    {
        memset(linker,0,sizeof(linker));
        memset(g, 0, sizeof(g));
        memset(vis,0,sizeof(vis));
        cin >> n;
        string Group,Sex;
        cin >> Group >> Sex;
        nx = n,ny = n;
        for(int i = 0; i < n; i++)
        {
            cin >> m;
            for (int j = 0; j < m; j++)
            {
                cin >> a;
                vis[i][a - 1] = 1;
                vis[a - 1][i] = 1;
            }
            for (int j = 0; j < n; j++)
            {
                if (!vis[i][j] && !vis[j][i] && Group[i] != Group[j])
                {
                    int ret = 20000;
                    if (Sex[i] == '0') ret++;
                    if (Sex[j] == '0') ret++;
                    if(Group[i] == '0' && Group[j] == '1') g[i][j] = ret;
                    else if (Group[i] == '1' && Group[j] == '0') g[j][i] = ret;
                }
            }
        }
        int res = KM();
        cout << (res / 20000) << " " << (res % 10000) << endl;
        for(int i = 0; i < n; i++)
        {
            if (linker[i] != -1 && g[linker[i]][i])
            {
                cout << linker[i] + 1 << " " << i + 1 << endl;
            }
        }
    }
    return 0;
}

/*
4
4
1010
0000
2 2 4
0
0
0
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值