传送门:洛谷-试题库问题
题意
假设一个试题库中有n道试题。每道试题都标明了所属类别。同一道题可能有多个类别属性。现要从题库中抽取m 道题组成试卷。并要求试卷包含指定类型的试题。试设计一个满足要求的组卷算法。 对于给定的组卷要求,计算满足要求的组卷方案。
输入
第1行有2个正整数k和n (2 <=k<= 20, k<=n<= 1000) k 表示题库中试题类型总数,n 表示题库中试题总数。第2 行有k 个正整数,第i 个正整数表示要选出的类型i的题数。这k个数相加就是要选出的总题数m。接下来的n行给出了题库中每个试题的类型信息。每行的第1 个正整数p表明该题可以属于p类,接着的p个数是该题所属的类型号。
输出
第i 行输出 “i:”后接类型i的题号。如果有多个满足要求的方案,只要输出1个方案。如果问题无解,则输出“No Solution!”。
题解
网络流建模问题。 我们先建立一个源点和一个汇点。 然后在每个类型号与源点之间连一条容量为要求题数的有向边。 对于每一道题,我们在该题与对应类型号之间连一条容量为1的有向边,并在该题与汇点之间也连一条容量为1的有向边。 接着跑最大流,一定要源点出发的每条边都流满了才有解。
代码
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cstdlib>
#include<queue>
#define INF 0x7fffffff
using namespace std;
const int N=
2e6+
10;
int t[
22],head[N],to[N],nxt[N],w[N],flow[N];
int d[N],n,k,cnt,T=
2e5,dir[N],vis[
1002];
queue<int>Q;
inline int read()
{
char ch=getchar();
int x=
0,t=
1;
while(ch>
'9' || ch<
'0') {
if(ch==
'-') t=-
1;ch=getchar();}
while(ch>=
'0' && ch<=
'9') {x=(x<<
3)+(x<<
1)+(ch^
48);ch=getchar();}
return x*t;
}
inline void link(
int u,
int v,
int cap)
{
to[++cnt]=v;nxt[cnt]=head[u];head[u]=cnt;w[cnt]=cap;
}
inline bool bfs()
{
memset(d,-
1,
sizeof(d));
d[
0]=
0;Q.push(
0);
while(!Q.empty()){
int now=Q.front();Q.pop();
for(
int i=head[now];i;i=nxt[i]){
if(d[to[i]]==-
1 && w[i]>flow[i]){
d[to[i]]=d[now]+
1;
Q.push(to[i]);
}
}
}
if(d[T]==-
1)
return false;
return true;
}
inline int min(
int x,
int y)
{
return x>y? y:x;
}
inline int dfs(
int st,
int ed,
int f)
{
if(st==ed)
return f;
int tot=
0;
for(
int i=head[st];i;i=nxt[i]){
if(d[to[i]]==d[st]+
1 && w[i]>flow[i]){
int ww=f-tot;
int qw=dfs(to[i],ed,min(w[i],ww));
if(qw>
0){
flow[i]+=qw;
flow[i^
1]-=qw;
tot+=qw;
if(tot==f)
return tot;
}
}
}
if(tot==
0) d[st]=-
1;
return tot;
}
inline void solve()
{
while(bfs()) dfs(
0,T,INF);
}
inline bool check()
{
for(
int i=head[
0];i;i=nxt[i]){
if(w[i]!=flow[i])
return false;
}
return true;
}
inline void print()
{
for(
int i=
1;i<=k;i++){
printf(
"%d: ",i);
for(
int j=head[i];j && t[i];j=nxt[j]){
if(!vis[to[j]]){
t[i]--;
vis[to[j]]=
1;
printf(
"%d ",to[j]-k);
}
}
printf(
"\n");
}
}
int main(){
k=read();n=read();
for(
int i=
1;i<=k;i++){
t[i]=read();link(
0,i,t[i]);link(i,
0,
0);
}
for(
int i=
1;i<=n;i++){
int op=read();
while(op--){
int in=read();
link(in,k+i,
1);link(k+i,in,
0);
link(k+i,T,
1);link(T,k+i,
0);
}
}
solve();
if(!check()){
printf(
"No Solution!\n");
return 0;
}
print();
return 0;
}