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; }