sgu-242 Student's Morning

59 篇文章 1 订阅
4 篇文章 0 订阅
题目大意:

N 个人,K个大学,每个人有 Gi 个心仪的学校,为集合 Si ,然后然后这 N 个人可以去任意一个且仅一个他心仪的学校,问是否可以使得K个学校每个学校都有不小于 2 个人去。如果没有,输出”NO”,否则输出” YES ”,然后接下来 K 行,每行一个数,表示去第i个学校的人有几个,接下来输出那几个人去哪个学校。 PS: 有的人可以不去学校,只要满足每个学校有 2 个及以上的人去就行了,不要求N个人每个人都去上学,比如可以在家浪。。。。。。

解题思路:

首先这道题目显然可以用有上下确界的可行流做,但是太麻烦了,直接跑最大流就行了啊。
首先原点 St 向所有的学生连一条流量为 1 的边,所有的学生向他心仪的学校连一条流量为1的边,然后每个学校向汇点 En 连一条流量为 2 的边,然后跑最大流,如果最后MaxFlow=K2说明是可行的,否则是不行的。

AC代码:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <iostream>
#define Min(a,b) ((a)>(b)?(b):(a))

using namespace std;

int G[510][510]={{0}};
int f[510][510]={{0}};
int N,K;
int st=0,en;
int dist[510]={0};
int dui[510]={0};
int duip=0;

void bfs()
{
    duip=0;
    dui[++duip]=en;
    for(int i=1;i<=duip;i++)
    {
        int u=dui[i];
        for(int v=0;v<=en;v++)
        {
            if(G[v][u]-f[v][u]>0 && dist[v]>dist[u]+1)
            {
                dist[v]=dist[u]+1;
                dui[++duip]=v;
            }
        }
    }
    return;
}

int Max_Flow(int now,int Max)
{
    if(now==en) return Max;
    int sum=0;
    for(int i=0;i<=en;i++)
    {
        if(G[now][i]-f[now][i]>0 && dist[now]==dist[i]+1)
        {
            int tmp=Min(G[now][i]-f[now][i],Max);
            tmp=Max_Flow(i,tmp);
            sum+=tmp;
            Max-=tmp;
            f[now][i]+=tmp;
            f[i][now]-=tmp;
            if(Max==0) return sum;
        }
    }
    return sum;
}

void prt()
{
    for(int i=N+1;i<=en-1;i++)
    {
        printf("2 ");
        for(int j=1;j<=N;j++)
            if(f[j][i]==1)
                printf("%d ",j);
        puts("");
    }
    return;
}

int main()
{
    scanf("%d%d",&N,&K);
    en=N+K+1;
    for(int i=1;i<=N;i++)
    {
        int num=0;
        scanf("%d",&num);
        for(int j=1;j<=num;j++)
        {
            int sb;
            scanf("%d",&sb);
            G[i][sb+N]=1;
        }
        G[st][i]=1;
    }
    for(int i=1;i<=K;i++)
        G[i+N][en]=2;
    int ans=0;
    for(;;)
    {
        memset(dist,0x3f3f3f3f,sizeof(dist));
        dist[en]=0;
        bfs();
        if(dist[st]==0x3f3f3f3f)
            break;
        ans+=Max_Flow(st,2e9);
        if(ans==K*2) break;
    }
    if(ans!=K*2)
        cout<<"NO"<<endl;
    else
    {
        cout<<"YES"<<endl;
        prt();
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值