[日常摸鱼]「网络流 24 题」试题库

https://loj.ac/problem/6006

题意:$n$道题每题有若干种类别,一共有$k$种类别,告诉你每种类别各自需要的题数,构造一种选题目的方案并输出方案。

虽然题目好像没说不过一道题应该不能选多次…(反正我这么写的过掉了x

这道题做下来感觉莫名的很爽233

把每道题向其对应的类别连容量为1的边,源点向所有题也连容量为1的边,而所有类别向汇点连的边容量为该题所需要的题数。

从源点到汇点跑一遍最大流,每次找到增广路我们要更新这条路上唯一的那个对应类别的结点,于是我们就想到用EK来实现这个过程啦。

设所有类别需要的题数之和为$m$,我们顺便求出了最大流,显然如果最后最大流<$m$的话就无解了。否则的话就输出方案~

我也不知道要不要排序反正随手排一个保平安…

#include<cstdio>
#include<cstring>
#include<algorithm>
#define rep(i,n) for(register int i=1;i<=n;i++)
#define REP(i,a,b) for(register int i=a;i<=b;i++)
using namespace std;
const int N=1050;
const int M=50005;
const int INF=(~0u>>1);
struct edge
{
    int to,nxt,w;
    edge(int to=0,int nxt=0,int w=0):to(to),nxt(nxt),w(w){}
}edges[M<<1];
int n,m,k,cnt,s,t,st,ed,maxflow;
int head[M<<1],infc[N],pre[N],ans[25][N],v[N],q[N],need[N];

inline void addEdge(int u,int v,int w=1)
{
    edges[++cnt]=edge(v,head[u],w);head[u]=cnt;
    edges[++cnt]=edge(u,head[v],0);head[v]=cnt;
}
#define cur edges[i].to
inline bool bfs()
{
    memset(v,0,sizeof v);v[s]=1;
    infc[s]=INF;st=ed=0;q[st++]=s;
    while(ed<st)
    {
        int k=q[ed++];
        for(register int i=head[k];i;i=edges[i].nxt)if(edges[i].w&&!v[cur])
        {
            v[cur]=1;q[st++]=cur;pre[cur]=i;
            infc[cur]=min(infc[k],edges[i].w);
            if(cur==t)return 1;
        }
    }
    return 0;
}
#undef cur
inline void update()
{
    int tmp=t;
    while(tmp!=s)
    {
        int i=pre[tmp];
        edges[i].w-=infc[t];
        edges[i^1].w+=infc[t];
        int now=tmp-n,cur=edges[i^1].to;
        if(1<=now&&now<=k)
            ans[now][++ans[now][0]]=cur;
        tmp=edges[i^1].to;
    }
    maxflow+=infc[t];
}
int main()
{
    scanf("%d%d",&k,&n);s=n+k+1;t=s+1;cnt=1;
    rep(i,k)scanf("%d",&need[i]),m+=need[i];
    rep(i,n)
    {
        int p,x;scanf("%d",&p);
        rep(j,p)
        {
            scanf("%d",&x);
            addEdge(i,x+n);
        }
    }
    rep(i,n)addEdge(s,i);
    rep(i,k)addEdge(i+n,t,need[i]);
    while(bfs())update();
    if(maxflow!=m)printf("No Solution!");
    else
    {
        rep(i,k)
        {
            sort(ans[i]+1,ans[i]+ans[i][0]+1);
            printf("%d: ",i);rep(j,ans[i][0])printf("%d ",ans[i][j]);
            printf("\n");
        }
    }
    return 0;
}

 

转载于:https://www.cnblogs.com/yoooshinow/p/8424689.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值