1. 程式人生 > >[日常摸魚]「網絡流 24 題」試題庫

[日常摸魚]「網絡流 24 題」試題庫

輸出 題目 flow void name amp date div ref

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

[日常摸魚]「網絡流 24 題」試題庫