题目:洛谷P2763。
题目大意:一个试题库有n道试题,现在要选m道题组成试卷。共有k种类型的题目,每种类型各需要若干题。每一种题目有一些不同的类型,但选择这道题只能选定它一个类型。现在要你设计一种试卷的组成方案。如果不能满足条件输出No Solution!
解题思路:网络流。
首先建立超级源点S和超级汇点T。
对于每个试题,从S向该试题连一条容量为1的边(因为每道试题只能用一次)。
对于每个试题对应的类型,从该试题分别向这些类型连一条容量为1的边。
对于每个类型,从该类型向T连一条边,容量为该类型所需要的题目数量。
然后做一遍最大流即可。
如果最大流不等于m,则说明无解,输出No Solution!
否则输出方案即可。
输出方案,我用了如下办法:
首先枚举题目类型,然后枚举与它有关的边。如果这条边连接的不是T(那么只可能是连向题目的反向边)且有剩余容量(说明正向的该条边被流过),则输出之。
然后让SPJ自己判断吧。
C++ Code:
#include<cstdio> #include<cstring> #include<cctype> #include<queue> #define M 5005 #define inf 0x3fffffff std::queue<int>q; int k,n,head[M],cnt=-1,level[M],iter[M]; struct edge{ int to,cap,nxt; }e[M*M]; inline int readint(){ char c=getchar(); for(;!isdigit(c);c=getchar()); int d=0; for(;isdigit(c);c=getchar())d=(d<<3)+(d<<1)+(c^'0'); return d; } inline void addedge(int u,int v,int flow){ ++cnt; e[cnt]=(edge){v,flow,head[u]}; head[u]=cnt; ++cnt; e[cnt]=(edge){u,0,head[v]}; head[v]=cnt; } void bfs(int s,int t){ level[s]=1; q.push(s); while(!q.empty()){ int u=q.front(); q.pop(); for(int i=head[u];i!=-1;i=e[i].nxt) if(level[e[i].to]==-1&&e[i].cap){ level[e[i].to]=level[u]+1; q.push(e[i].to); } } } int dfs(int u,int t,int f){ if(u==t)return f; for(int& i=iter[u];i!=-1;i=e[i].nxt) if(level[e[i].to]>level[u]&&e[i].cap){ int d=dfs(e[i].to,t,f>e[i].cap?e[i].cap:f); if(d){ e[i].cap-=d; e[i^1].cap+=d; return d; } } return 0; } int maxflow(int s,int t){ for(int flow=0;;){ memset(level,-1,sizeof level); bfs(s,t); if(level[t]==-1)return flow; memcpy(iter,head,sizeof iter); int f; while(f=dfs(s,t,inf))flow+=f; } } int main(){ k=readint(),n=readint(); memset(head,-1,sizeof head); int all=0; for(int i=1;i<=k;++i){ int p=readint(); all+=p; addedge(i+n,k+n+1,p); } for(int i=1;i<=n;++i){ addedge(0,i,1); for(int p=readint();p--;){ int x=readint(); addedge(i,x+n,1); } } int mf; if((mf=maxflow(0,n+k+1))!=all)return!printf("No Solution! "); for(int i=n+1;i<=k+n;++i){ printf("%d:",i-n); for(int j=head[i];j!=-1;j=e[j].nxt) if(e[j].to<=n&&e[j].cap)printf(" %d",e[j].to); putchar(' '); } return 0; }