P2763 试题库问题
题目描述
«问题描述:
假设一个试题库中有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: 复制
3 15 3 3 4 2 1 2 1 3 1 3 1 3 1 3 3 1 2 3 2 2 3 2 1 3 1 2 1 2 2 1 2 2 1 3 2 1 2 1 1 3 1 2 3
输出样例#1: 复制
1: 1 6 8 2: 7 9 10 3: 2 3 4 5
说明
感谢 @PhoenixEclipse 提供spj
//感觉做此题最大的障碍就是看不下题面去233 //开一个超级源点,向每种试卷类型连边,边权为这种类型需要的试卷数量 //然后试卷类型向试卷连边,边权为1 //试卷向超级汇点连边,边权为1 //怎么输出方案。。。 //emmmmm.......想想 //枚举每种试卷类型,看看与它相连的试卷的边的边权==0? //......试一下
//OK,过了
#include<iostream> #include<cstdio> #include<cmath> #include<cstring> #include<string> #include<algorithm> #include<queue> using namespace std; inline int read() { char c=getchar();int num=0; for(;!isdigit(c);c=getchar()); for(;isdigit(c);c=getchar()) num=num*10+c-'0'; return num; } const int N=1e4+5; int k,n,sum,S,T; int head[N],num_edge; struct Edge { int v,flow,nxt; }edge[N]; inline void add_edge(int u,int v,int flow) { edge[++num_edge].v=v; edge[num_edge].flow=flow; edge[num_edge].nxt=head[u]; head[u]=num_edge; } int dep[N],from[N]; bool bfs() { queue<int> que; que.push(S); for(int i=S;i<=T;++i) from[i]=head[i],dep[i]=0; dep[S]=1; int now,v; while(!que.empty()) { now=que.front(),que.pop(); for(int i=head[now];i;i=edge[i].nxt) { v=edge[i].v; if(!dep[v]&&edge[i].flow) { dep[v]=dep[now]+1; if(v==T) return 1; que.push(v); } } } return 0; } int dfs(int u,int flow) { if(u==T||!flow) return flow; int outflow=0,tmp; for(int i=head[u],v;i;i=edge[i].nxt) { v=edge[i].v; if(dep[v]!=dep[u]+1||!edge[i].flow) continue; tmp=dfs(v,min(flow,edge[i].flow)); if(!tmp) continue; edge[i].flow-=tmp; edge[i^1].flow+=tmp; flow-=tmp; outflow+=tmp; if(!flow) return outflow; } dep[u]=-1; return outflow; } int paper[20][N]; int main() { num_edge=1; k=read(),n=read(); for(int i=1,x;i<=k;++i) { x=read(),sum+=x; add_edge(S,i,x); add_edge(i,S,0); } T=sum+n+1; for(int i=sum+1,p,x;i<=n+sum;++i) { p=read(); while(p--) { x=read(); add_edge(x,i,1); add_edge(i,x,0); } add_edge(i,T,1); add_edge(T,i,0); } int ans=0; while(bfs()) { ans+=dfs(S,0x7fffffff); } if(ans!=sum) puts("No Solution!"); else { for(int A=1;A<=k;++A) { for(int i=head[A],v;i;i=edge[i].nxt) { v=edge[i].v; if(v==S) continue; if(!edge[i].flow) paper[A][++paper[0][A]]=v-sum; } } for(int i=1;i<=k;++i) { printf("%d: ",i); for(int j=1;j<=paper[0][i];++j) { printf("%d ",paper[i][j]); } puts(""); } } return 0; }