POJ 1469 COURSES(二分图匹配)

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

Description

Consider a group of N students and P courses. Each student visits zero, one or more than one courses. Your task is to determine whether it is possible to form a committee of exactly P students that satisfies simultaneously the conditions: 

  • every student in the committee represents a different course (a student can represent a course if he/she visits that course) 
  • each course has a representative in the committee 

Input

Your program should read sets of data from the std input. The first line of the input contains the number of the data sets. Each data set is presented in the following format: 

P N 
Count1 Student 1 1 Student 1 2 ... Student 1 Count1 
Count2 Student 2 1 Student 2 2 ... Student 2 Count2 
... 
CountP Student P 1 Student P 2 ... Student P CountP 

The first line in each data set contains two positive integers separated by one blank: P (1 <= P <= 100) - the number of courses and N (1 <= N <= 300) - the number of students. The next P lines describe in sequence of the courses �from course 1 to course P, each line describing a course. The description of course i is a line that starts with an integer Count i (0 <= Count i <= N) representing the number of students visiting course i. Next, after a blank, you抣l find the Count i students, visiting the course, each two consecutive separated by one blank. Students are numbered with the positive integers from 1 to N. 
There are no blank lines between consecutive sets of data. Input data are correct. 

Output

The result of the program is on the standard output. For each input data set the program prints on a single line "YES" if it is possible to form a committee and "NO" otherwise. There should not be any leading blanks at the start of the line.

Sample Input

2
3 3
3 1 2 3
2 1 2
1 1
3 3
2 1 3
2 1 3
1 1

Sample Output

YES
NO


题目大意:m个课程,n个学生,接下来m行,每行一个数cnt,接cnt个数,表示有cnt个编号为y的学生选了这门课,问能否找出一种关系,每一门课都有一个课代表(选了该门课的学生才能当且每个学生最多担任一门课程课代表)


题目思路:二分图最大匹配,看是否能得到m条匹配边,匈牙利算法和Hopcroft-Carp算法都可以,但差距就出来了,前者900多ms,后者500多ms(注意:不要cin,cin好像会超时)

代码:

/*#include<cstdio>
#include<cmath>
#include<cstring>
#include<string>
#include<cstdlib>
#include<algorithm>
#include<iostream>
#include<queue>
#include<stack>
#include<map>

using namespace std;

#define FOU(i,x,y) for(int i=x;i<=y;i++)
#define FOD(i,x,y) for(int i=x;i>=y;i--)
#define MEM(a,val) memset(a,val,sizeof(a))
#define PI acos(-1.0)

const double EXP = 1e-9;
typedef long long ll;
typedef unsigned long long ull;
const int INF = 0x3f3f3f3f;
const ll MINF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9+7;
const int N = 505;

int line[N][N];  //i,j之间是否存在关系,即可以匹配,记得初始化
int used[N];     //每一轮寻找增广路径时第i个妹子是否被使用过
int Next[N];     //Next[i]=x代表第i个妹子和x号男生是匹配的,记得初始化
int n,m;

bool Find(int x)
{
    for(int i=1;i<=m;i++)
    {
        if(line[x][i]&&!used[i])
        {
            used[i] = 1;             //第i个妹子被使用了
            if(Next[i]==0||Find(Next[i]))   //判断第i个妹子是否有了男朋友,没有的话就可以匹配,有的话就把next[i]号男生转移掉再匹配
            {
                Next[i] = x;
                return true;
            }
        }
    }
    return false;
}

int maxMatch()
{
    memset(Next,0,sizeof(Next));
    int sum = 0;
    for(int i=1;i<=n;i++)
    {
        memset(used,0,sizeof(used));
        if(Find(i))
            sum++;
    }
    return sum;
}

int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    std::ios::sync_with_stdio(false);
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&m,&n);
        MEM(line,0);
        for(int i=1;i<=m;i++)
        {
            int x,y;
            scanf("%d",&x);
            while(x--)
            {
                scanf("%d",&y);
                line[y][i]=1;
            }
        }
        int ans = maxMatch();
        if(ans==m)
            puts("YES");
        else
            puts("NO");
    }
    return 0;
}
*/

#include<cstdio>
#include<cmath>
#include<cstring>
#include<string>
#include<cstdlib>
#include<algorithm>
#include<iostream>
#include<queue>
#include<stack>
#include<map>

using namespace std;

#define FOU(i,x,y) for(int i=x;i<=y;i++)
#define FOD(i,x,y) for(int i=x;i>=y;i--)
#define MEM(a,val) memset(a,val,sizeof(a))
#define PI acos(-1.0)

const double EXP = 1e-9;
typedef long long ll;
typedef unsigned long long ull;
const int INF = 0x3f3f3f3f;
const ll MINF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9+7;
const int N = 505;

//时间复杂度O(sqrt(n)*E)

const int MAXN=350;// 最大点数
int bmap[MAXN][MAXN];//二分图
int cx[MAXN];//cx[i]表示左集合i顶点所匹配的右集合的顶点序号
int cy[MAXN]; //cy[i]表示右集合i顶点所匹配的左集合的顶点序号
int dis;
int n,m;
int dx[MAXN],dy[MAXN];  //dx表示到x的距离,dy表示到y的距离
int used[MAXN];        //在每次增广中是否使用i点

bool SearchPath()   //bfs寻找增广路集
{
    queue<int>Q;
    dis = INF;  //存每一次增广的距离
    memset(dx,-1,sizeof(dx));
    memset(dy,-1,sizeof(dy));
    for(int i=1;i<=n;i++)
    {
        if(cx[i]==-1)  ////将未遍历的节点入队,并初始化次节点距离为0
        {
            Q.push(i);
            dx[i] = 0;
        }
    }
    while(!Q.empty())
    {
        int u = Q.front();
        Q.pop();
        if(dx[u]>dis)
            break;
        //取右边的节点
        for(int v=1;v<=m;v++)
        {
            if(bmap[u][v]&&dy[v]==-1)
            {
                dy[v] = dx[u] + 1;  //v的距离为u的对应距离+1
                if(cy[v]==-1)       //如果该点未匹配,增广路形成
                    dis = dy[v];
                else                //如果该点已匹配,那么接着往下搜
                {
                    dx[cy[v]] = dy[v] + 1;
                    Q.push(cy[v]);
                }
            }
        }
    }
    return dis!=INF;
}

bool DFS(int u)
{
    for(int v=1;v<=m;v++)
    {
        //如果该点没有被使用过,并且距离为上一节点+1
        if(!used[v]&&bmap[u][v]&&dy[v]==dx[u]+1)
        {
            used[v] = 1;  //标记使用过该点
            //如果该点已经被匹配了并且为最后一个匹配点,那么这条路径不是增广路,即这条路的结点已经匹配
            if(cy[v]!=-1&&dy[v]==dis)
                continue;
            if(cy[v]==-1||DFS(cy[v]))
            {
                cy[v]=u,cx[u]=v;
                return true;
            }
        }
    }
    return false;
}

int MaxMatch()
{
    int sum = 0;
    memset(cx,-1,sizeof(cx));
    memset(cy,-1,sizeof(cy));
    while(SearchPath()) //当存在增广路,继续松弛
    {
        memset(used,0,sizeof(used));   //每一次的右边的点是否用过
        for(int i=1;i<=n;i++)
        {
            if(cx[i]==-1&&DFS(i))    //如果当前这个点没连过,且能找到增宽路
                sum++;
        }
    }
    return sum;
}


int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    std::ios::sync_with_stdio(false);
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&m,&n);
        MEM(bmap,0);
        for(int i=1;i<=m;i++)
        {
            int x,y;
            scanf("%d",&x);
            while(x--)
            {
                scanf("%d",&y);
                bmap[y][i]=1;
            }
        }
        int ans = MaxMatch();
        if(ans==m)
            puts("YES");
        else
            puts("NO");
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/baodream/article/details/80067912