[网络流24题]试题库问题

题目:洛谷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;
}
原文地址:https://www.cnblogs.com/Mrsrz/p/8244501.html