题目描述
«问题描述:
假设一个试题库中有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
ac代码:
二分图多重匹配,首先起点到类型点连边流量为a[i],然后每个试题和他所属的类型以及终点连边,流量为1。输出路径,只要便利每个流量点的出边看看流量是否为0,还要排除反向边。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<string>
#include<vector>
#include<bitset>
#include<time.h>
using namespace std;
typedef long long ll;
struct node{
int u,v,nxt,w;
}e[100005<<1];
int tot=1,head[10050];//tot从1开始,双向边异或
int st,ed;
int dis[10050],q[10050];
void add(int u,int v,int w){
e[++tot].v=v;
e[tot].u=u;
e[tot].w=w;
e[tot].nxt=head[u];
head[u]=tot;
}
int bfs(int st,int ed){
memset(dis,-1,sizeof(dis));
int front=0,tail=0;
q[tail++]=st;
dis[st]=0;
while(front<tail){
int cur=q[front++];
if(cur==ed) return 1;
for(int i=head[cur];i!=-1;i=e[i].nxt){
if(e[i].w&&dis[e[i].v]<0){
q[tail++]=e[i].v;
dis[e[i].v]=dis[cur]+1;
}
}
}
if(dis[ed]==-1) return 0;
return 1;
}
int dfs(int cur,int lim){
if(lim==0||cur==ed) return lim;
int w,flow=0;
for(int i=head[cur];i!=-1;i=e[i].nxt){
if(e[i].w&&dis[e[i].v]==dis[cur]+1){
w=dfs(e[i].v,min(lim,e[i].w));
e[i].w-=w;
e[i^1].w+=w;
flow+=w;
lim-=w;
if(lim==0) break;
}
}
if(!flow) dis[cur]=-1;
return flow;
}
int dinic(){
int ans=0;
while(bfs(st,ed))
ans+=dfs(st,0x7fffffff);
return ans;
}
int main(){
int k,n,w,num,m=0;
scanf("%d%d",&k,&n);
for(int i=0;i<=n+k+5;i++) head[i]=-1;
for(int i=1;i<=k;i++){
scanf("%d",&w);
m+=w;
add(st,i,w);
add(i,st,0);
}
for(int i=1;i<=n;i++){
scanf("%d",&num);
for(int j=1;j<=num;j++){
scanf("%d",&w);
add(w,k+i,1);
add(k+i,w,0);
}
}
ed=n+k+1;
for(int i=1;i<=n;i++){
add(k+i,ed,1);
add(ed,k+i,0);
}
int tmp=dinic();
if(tmp==m){
for(int i=1;i<=k;i++){
printf("%d:",i);
for(int j=head[i];j!=-1;j=e[j].nxt){
if(e[j].w==0&&e[j].v>e[j].u){//注意去除反向边
printf(" %d",e[j].v-k);
}
}
puts("");
}
}
else puts("No Solution!");
}