【24题】试题库问题【网络流】

题目大意:

假设一个试题库中有n道试题。每道试题都标明了所属类别。同一道题可能有多个类别属性。现要从题库中抽取m道题组成试卷。并要求试卷包含指定类型的试题。


思路:

这道题很明显是一个二分图。所以首选Dinic
对于每一个试卷,将它连向汇点T,流量为所需的题目总数;而每一道题就连向原点S,流量为1。而中间则将每一道题连向它可以放入的试卷,流量为1。之后跑一边Dinic,若sum=num则可以组成试卷,否则不行。


代码:

#include <cstdio>
#include <queue>
#include <iostream>
#include <cstring>
#define Inf 0x7f
#define INF 2147483647
using namespace std;

int n,m,k,s,t,head[3000001],dep[3000001],num,x,y,sum,ans;

struct edge
{
    int to,next,c;
}e[3000001];

void add(int from,int to,int c)
{
    k++;
    e[k].c=c;
    e[k].to=to;
    e[k].next=head[from];
    head[from]=k;
}

bool bfs()  //分层不解释
{
    memset(dep,Inf,sizeof(dep));    
    dep[s]=0;
    queue<int> q;
    q.push(s);
    while (q.size())
    {
        int u=q.front();
        q.pop();
        for (int i=head[u];i;i=e[i].next)
        {
            int v=e[i].to;
            if (dep[u]+1<dep[v]&&e[i].c)
            {
                dep[v]=dep[u]+1;
                q.push(v);
            }
        }
    }
    return (dep[t]<Inf);
}

int dfs(int u,int low)
{
    int lows=0;
    if (u==t) return low;  //到达汇点
    for (int i=head[u];i;i=e[i].next)
    {
        int v=e[i].to;
        if (dep[v]==dep[u]+1&&e[i].c)
        {
            lows=dfs(v,min(e[i].c,low));  //求最小边权
            if (!lows) continue;  //最小边权为0
            e[i].c-=lows;  //正向边
            e[((i+1)^1)-1].c+=lows;  //反向边
            return lows;
        }
    }
    return 0;
}

bool print()  //输出不解释
{
    for (int i=n+1;i<=n+m;i++)
    {
        printf("%d:",i-n);
        for (int j=head[i];j;j=e[j].next)
        {
            int v=e[j].to;
            if (v<t&&e[j].c)
             printf(" %d",v);
        }
        putchar(10); 
    }
    return false;
}

int main()
{
    scanf("%d%d",&m,&n);
    s=0;
    t=n+m+1;
    for (int i=1;i<=m;i++)
    {
        scanf("%d",&x);
        num+=x;
        add(n+i,t,x);
        add(t,n+i,0);
    }
    for (int j=1;j<=n;j++)
    {
        add(s,j,1);
        add(j,s,0);
        scanf("%d",&x);
        for (int i=1;i<=x;i++)
        {
            scanf("%d",&y);
            add(j,n+y,1);
            add(n+y,j,0);
        }
    }
    while (bfs())  //只要能分层就循环
    {
        while (sum=dfs(s,INF))
         ans+=sum;
    }
    if (ans<num) return printf("No Solution!\n")&0;
    return print();
}
posted @ 2018-08-04 09:47  全OI最菜  阅读(83)  评论(0编辑  收藏  举报