[网络流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;
}