编程练习B(最大二部图匹配)

A:Kindergarten(POJ3692)

Time Limit:2000MS Memory Limit:65536KB 64bit IO Format:%lld & %llu

Description

In a kindergarten, there are a lot of kids. All girls of the kids know each other and all boys also know each other. In addition to that, some girls and boys know each other. Now the teachers want to pick some kids to play a game, which need that all players know each other. You are to help to find maximum number of kids the teacher can pick.

Input

The input consists of multiple test cases. Each test case starts with a line containing three integers
G, B (1 ≤ G, B ≤ 200) and M (0 ≤ M ≤ G × B), which is the number of girls, the number of boys and
the number of pairs of girl and boy who know each other, respectively.
Each of the following M lines contains two integers X and Y (1 ≤ X≤ G,1 ≤ Y ≤ B), which indicates that girl X and boy Y know each other.
The girls are numbered from 1 to G and the boys are numbered from 1 to B.

The last test case is followed by a line containing three zeros.

Output

For each test case, print a line containing the test case number( beginning with 1) followed by a integer which is the maximum number of kids the teacher can pick.

Sample Input

2 3 3
1 1
1 2
2 3
2 3 5
1 1
1 2
2 1
2 2
2 3
0 0 0

Sample Output

Case 1: 3
Case 2: 4

想法

摘自:
http://www.cnblogs.com/kuangbin/archive/2012/08/19/2646020.html
幼儿园有g个女孩和b个男孩,同性之间互相认识,而且男孩和女孩之间有的也互相认识。现在要选出来最多的孩子,他们之间都互相认识。
一道基础的二分图最大独立集问题。
二分图的最大独立集 = n-最小覆盖集 = n-完美匹配数。
所以就转化成了二分图匹配,用匈牙利算法实现即可。
(模版题)

我的代码

//二分图匹配(匈牙利算法的DFS实现)
//初始化:g[][]两边顶点的划分情况
//建立g[i][j]表示i->j的有向边就可以了,是左边向右边的匹配
//g没有边相连则初始化为0
//uN是匹配左边的顶点数,vN是匹配右边的顶点数
//调用:res=hungary();输出最大匹配数
//优点:适用于稠密图,DFS找增广路,实现简洁易于理解
//时间复杂度:O(VE)
//***************************************************************************/
//顶点编号从0开始的
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<algorithm>
using namespace std;
const int MAXN=510;
int T,m;
int uN,vN;//u,v数目
int g[MAXN][MAXN];
int vx[MAXN];
int vy[MAXN];
bool used[MAXN];
void init(){
    for (int i = 0; i < MAXN; i++) {
        for (int j = 0; j < MAXN; j++) {
            g[i][j] = 1;
        }
    }
}
bool dfs(int u)//从左边开始找增广路径
{
    int v;
    for(v=0;v<vN;v++)//这个顶点编号从0开始,若要从1开始需要修改
        if(g[u][v]&&!used[v])
        {
            used[v]=true;
            if(vy[v]==-1||dfs(vy[v]))
            {//找增广路,反向
                vy[v]=u;
                vx[u]=v;
                return true;
            }
        }
    return false;//这个不要忘了,经常忘记这句
}
int hungary()
{
    int res=0;
    int u;
    memset(vx,-1,sizeof(vx));
    memset(vy,-1,sizeof(vy));
    for(u=0;u<uN;u++)
    {
        if(vx[u]==-1){
            memset(used,0,sizeof(used));
            if(dfs(u)) res++;
        }
    }
    return res;
}
int main() {
   //  freopen("input.txt", "r", stdin);
    T = 0;
    while(scanf("%d%d%d",&uN,&vN,&m)!=EOF) {
        if ( uN == 0 && vN == 0 && m == 0) {
            break;
        }
        T++;
        init();
        int a,b;
        for (int i = 0; i < m; i++) {
            scanf("%d%d",&a,&b);
            a--,b--;
            g[a][b] = 0;
        }
        printf("Case %d: %d\n",T,uN+vN-hungary());
    }
    return 0;
}

B:Shooting Contest

Description

Welcome to the Annual Byteland Shooting Contest. Each competitor will shoot to a target which is a rectangular grid. The target consists of r*c squares located in r rows and c columns. The squares are coloured white or black. There are exactly two white squares and r-2 black squares in each column. Rows are consecutively labelled 1,..,r from top to bottom and columns are labelled 1,..,c from left to right. The shooter has c shots.

A volley of c shots is correct if exactly one white square is hit in each column and there is no row without white square being hit. Help the shooter to find a correct volley of hits if such a volley exists.
Example
Consider the following target:
这里写图片描述

Volley of hits at white squares in rows 2, 3, 1, 4 in consecutive columns 1, 2, 3, 4 is correct.
Write a program that: verifies whether any correct volley of hits exists and if so, finds one of them.

Input

The first line of the input contains the number of data blocks x, 1 <= x <= 5. The following lines constitute x blocks. The first block starts in the second line of the input file; each next block starts directly after the previous one.

The first line of each block contains two integers r and c separated by a single space, 2 <= r <= c <= 1000. These are the numbers of rows and columns, respectively. Each of the next c lines in the block contains two integers separated by a single space. The integers in the input line i + 1 in the block, 1 <= i <= c, are labels of rows with white squares in the i-th column.

Output

For the i-th block, 1 <= i <= x, your program should write to the i-th line of the standard output either a sequence of c row labels (separated by single spaces) forming a correct volley of hits at white squares in consecutive columns 1, 2, …, c, or one word NO if such a volley does not exists.

Sample Input

2
4 4
2 4
3 4
1 3
1 4
5 5
1 5
2 4
3 4
2 4
2 3

Sample Output

2 3 1 4
NO

解题思路:

摘自:
http://blog.csdn.net/wangjian8006/article/details/7977710
很容易想到二分匹配,我们将白色格子的行指向列,然后求最大匹配,用行去匹配列,看是否能够得到的匹配数是r,如果是r的话则代表所有的行都可以被打到,然后每列对应的行,如果匹配数不是r就输出NO
当然有特例,如果r>c,这种情况开c抢根本不可能达到r行,所以直接输出NO

我的代码

/*
 很容易想到二分匹配,我们将白色格子的行指向列,
 然后求最大匹配,用行去匹配列,看是否能够得到的匹配数是r,
 如果是r的话则代表所有的行都可以被打到,然后每列对应的行,如果匹配数不是r就输出NO
 当然有特例,如果r>c,这种情况开c抢根本不可能达到r行,所以直接输出NO
 */
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<vector>
#include<stack>
#include<bitset>
#include<cstdlib>
#include<cmath>
#include<set>
#include<list>
#include<deque>
#include<map>
#include<queue>
using namespace std;
const int MAXN=1005;
int T;
int r,c;
int uN,vN;//u,v数目
int g[MAXN][MAXN];
int linkerx[MAXN];
int linkery[MAXN];
bool used[MAXN];
bool dfs(int u)//从左边开始找增广路径
{
    int v;
    for(v=0;v<c;v++)//这个顶点编号从0开始,若要从1开始需要修改
        if(g[u][v]&&!used[v])
        {
            used[v]=true;
            if(linkery[v]==-1||dfs(linkery[v]))
            {//找增广路,反向
                linkery[v]=u;
                linkerx[u]=v;
                return true;
            }
        }
    return false;//这个不要忘了,经常忘记这句
}
int hungary()
{
    int res=0;
    int u;
    memset(linkerx,-1,sizeof(linkerx));
    memset(linkery,-1,sizeof(linkery));
    for(u=0;u<r;u++)
    {
        if(linkerx[u]==-1){
            memset(used,0,sizeof(used));
            if(dfs(u)) res++;
        }
    }
    return res;
}

void init(){
    memset(g, 0, sizeof(g));
    memset(used, 0, sizeof(used));
}
void input(){
    scanf("%d%d", &r, &c);
    int row1, row2;
    for (int i = 0; i < c; i++) {
        scanf("%d%d", &row1, &row2);
        row1--,row2--;
        g[row1][i] = 1,g[row2][i] = 1;
    }
}

void solve(){
    int result = hungary();
    if (r > c ||result != r) {
        printf("NO\n");
        return;
    }
    if (c == r) {
        printf("%d", linkery[0]+1);
        for (int i = 1; i < c; i++) {
            printf(" %d", linkery[i]+1);
        }
        printf("\n");
        return ;
    }else{
        for (int i = 0; i < c; i++) {
            if (linkery[i] != -1) {
                if (i==c-1) {
                    printf("%d", linkery[i]+1);
                }else{
                    printf("%d ", linkery[i]+1);
                }
            }else{
                for (int j = 0; j < r; j++) {
                    if (g[j][i] == 1) {
                        if (i==c-1) {
                            printf("%d", j+1);
                        }else{
                            printf("%d ", j+1);
                        }
                        break;
                    }
                }
            }
        }
        printf("\n");
        return;
    }
}

int main() {
    //freopen("input.txt", "r", stdin);
    scanf("%d",&T);
    while(T--) {
        init();
        input();
        solve();
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值