zoukankan      html  css  js  c++  java
  • 【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();
    }
  • 相关阅读:
    通用XML读写和配置(二)
    C++多态中的VPTR
    如何查看Linux操作系统的位数?
    另类获取ORACLE导入导出(imp/exp)数据的进度信息
    成熟是明亮而不刺眼的光辉
    C/C++预处理运算符
    系统设计与规划一点总结
    Linux 任务计划之crontab命令
    linux挂载磁盘阵列
    Linux下JDK的中文显示
  • 原文地址:https://www.cnblogs.com/hello-tomorrow/p/9417969.html
Copyright © 2011-2022 走看看