题目:洛谷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!\n");
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('\n');
}
return 0;
}