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

试题库问题

题目大意

给定 \(k\) 个题目类型和 \(n\) 个题目。每个题目会对应 \(sign\) 个类型,但是最终每个题目只能归属于一种类型,且只能被选择一次。

给出对于每种类型的题目所需求的数量,求出一种方案使其成立。

分析

建图

此题如果去掉输出方案,其实就是一个二分图匹配网络流的模板题,很容易发现,可以把题目编号和题目类型分为二分图的两边,从而进行匹配。

具体建图过程如下:

  • 建立一个超级源点和一个超级汇点,超级源点向表示题目的点各连接一条流量为 \(1\) 的边,而表示题目类型的点向超级汇点同样个连接一条流量为 \(1\) 的边。

  • 对于每个题目匹配的每种类型,分别有每个题目连向其所匹配类型一条流量为 \(1\) 的边。

如下图:

然后再跑一下最大流就行了

输出

对于输出,很显然匹配的个数必须要等于每种类型需求的个数之和。

那么我们只需要从每种类型倒回去找到每条连接他且流量为 \(0\) 的边,这就是我们要找的被选到的类型,然后再按照他的要求输出就行了。

CODE

#include <bits/stdc++.h>
#define int long long 
using namespace std; 
const int N=1e3+10;
int n,k,s,t;
inline int read()
{
    int s=0,w=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
    return s*w;
}
int tot=-1,v[N*N],w[N*N],nex[N*N],first[2*N];
inline void Add(int x,int y,int z)
{
	nex[++tot]=first[x];
	first[x]=tot;
	v[tot]=y,w[tot]=z;
}
int head[2*N],deep[N]; 
inline bool BFS()
{
	memset(deep,-1,sizeof(deep));
	queue<int> q;
	q.push(s);
	deep[s]=0;
	while(!q.empty()){
		int now=q.front(); q.pop();
		for(register int i=first[now];i!=-1;i=nex[i]){
			int to=v[i];
			if(w[i]>0&&deep[to]==-1) deep[to]=deep[now]+1,q.push(to);
		}
	}
	return deep[t]!=-1; //如果还能有"流"到达终点 
}
inline int DFS(int S,int T,int flow)
{
	if(S==T) return flow;
	int res=0;
	for(register int& i=head[S];i!=-1;i=nex[i]){
		int to=v[i];
		if(deep[to]==deep[S]+1){ //只往下一层流动 
			int temp=DFS(to,T,min(flow,w[i])); //记录该路径可流向终点的值 
			//反悔操作,可以重新延目标点得到相同的流量返回源点 
			w[i]-=temp,w[i^1]+=temp;
			flow-=temp; //该节点当前流量减掉向该路径流走的流量 
			res+=temp; //该路径走通了,返回的答案加上到达终点的流量 
			if(!flow) return res;
		}
	}
	return res;
}
inline int dinic()
{
	int res=0;
	while(BFS()){
		memcpy(head,first,sizeof(first));
		res+=DFS(s,t,1e9);
	}
	return res;
}
signed main()
{
	memset(first,-1,sizeof(first));
	k=read(),n=read();
	int sum=0;
	s=0,t=n+k+1;
	//题号与超级源点 
	for(register int i=1;i<=n;i++) Add(s,i,1),Add(i,s,0);
	//类别与超级汇点 
	for(register int i=1;i<=k;i++) { int num=read(); Add(i+n,t,num),Add(t,i+n,0); sum+=num; } 
	for(register int i=1;i<=n;i++){
		int sign=read();
		for(register int j=1;j<=sign;j++){
			int x=read();
			Add(i,x+n,1),Add(x+n,i,0); //题号与类别连边 
		}
	}
	if(dinic()==sum){
		for(register int now=1;now<=k;now++){
			printf("%lld: ",now);
			for(register int i=first[now+n];i!=-1;i=nex[i])
				if(w[i]&&v[i]!=t) printf("%lld ",v[i]);
			printf("\n");
		}
	}
	else printf("No Solution!\n"); 
	return 0;
}
posted @ 2021-02-28 21:30  ╰⋛⋋⊱๑落叶๑⊰⋌⋚╯  阅读(42)  评论(0编辑  收藏  举报