«问题描述:
假设一个试题库中有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!”。
思路:0号点为源点,1~n为问题,n+1~n+k为问题所属的类别,n+1+k为汇点,源点向每个问题建立容量为一的边,问题向所属类别建立容量为一的边,类别向汇点建立容量为要求此列题目数量的边,如果最大流==要求的总题目数量就可以,输出的时候直接判断边的容量。
#include<bits/stdc++.h>
#define read(x) scanf("%d",&x)
using namespace std;
const int inf=1000000000;
const int maxn=20000,maxm=1e6+10;
struct Edge{
int v,f,nxt;
};
int src,sink;
int g[maxn+10];
int nume;
Edge e[maxm*2+10];
void addedge(int u,int v,int c) {
e[++nume].v=v;
e[nume].f=c;
e[nume].nxt=g[u];
g[u]=nume;
e[++nume].v=u;
e[nume].f=0;
e[nume].nxt=g[v];
g[v]=nume;
}
void init() {
memset(g,0,sizeof(g));
nume=1;
}
queue<int> que;
bool vis[maxn+10];
int dist[maxn+10];
int n,m,k,dfn,sum;
int h[maxn];
bool bfs() {
memset(dist,0,sizeof(dist));
while(!que.empty())que.pop();
vis[src]=true;
que.push(src);
while(!que.empty()) {
int u=que.front();
que.pop();
for(int i=g[u];i;i=e[i].nxt) {
if(e[i].f && !vis[e[i].v]) {
que.push(e[i].v);
dist[e[i].v]=dist[u]+1;
vis[e[i].v]=true;
}
}
}
return vis[sink];
}
int dfs(int u,int delta) {
if(u==sink) {
return delta;
}
else {
int ret=0;
for(int i=g[u];delta&&i;i=e[i].nxt) {
if(e[i].f && dist[e[i].v]==dist[u]+1) {
int dd=dfs(e[i].v,min(e[i].f,delta));
e[i].f-=dd;
e[i^1].f+=dd;
delta-=dd;
ret+=dd;
}
}
return ret;
}
}
int maxflow() {
int ret=0;
while(true) {
memset(vis,0,sizeof(vis));
bfs();
if(!vis[sink]) return ret;
ret+=dfs(src,inf);
}
}
int to[maxn];
int in[maxn];
int a[maxn];
int main() {
init();
scanf("%d%d",&k,&n);
src=0;
sink=k+n+1;
sum=0;
for(int i=1;i<=k;i++) {
int tmp;
read(a[i]);
sum+=a[i];
addedge(n+i,sink,a[i]);
}
for(int i=1;i<=n;i++) {
int num;
read(num);
addedge(src,i,1);
for(int j=1;j<=num;j++) {
int tmp;
read(tmp);
addedge(i,n+tmp,1);
}
}
if(maxflow() == sum) {
for(int i=1;i<=k;i++) {
printf("%d:",i);
for(int j=g[i+n];j;j=e[j].nxt) {
int v=e[j].v;
int w=e[j].f;
if(w && v!=sink) {
printf("%d ",v);
}
}
puts("");
}
}
else {
puts("No Solution!");
}
}