题目大意:
假设一个试题库中有n道试题。每道试题都标明了所属类别。同一道题可能有多个类别属性。现要从题库中抽取m道题组成试卷。并要求试卷包含指定类型的试题。
思路:
这道题很明显是一个二分图。所以首选Dinic。
对于每一个试卷,将它连向汇点T,流量为所需的题目总数;而每一道题就连向原点S,流量为1。而中间则将每一道题连向它可以放入的试卷,流量为1。之后跑一边Dinic,若sum=num则可以组成试卷,否则不行。
代码:
#include <cstdio> #include <queue> #include <iostream> #include <cstring> #define Inf 0x7f #define INF 2147483647 using namespace std; int n,m,k,s,t,head[3000001],dep[3000001],num,x,y,sum,ans; struct edge { int to,next,c; }e[3000001]; void add(int from,int to,int c) { k++; e[k].c=c; e[k].to=to; e[k].next=head[from]; head[from]=k; } bool bfs() //分层不解释 { memset(dep,Inf,sizeof(dep)); dep[s]=0; queue<int> q; q.push(s); while (q.size()) { int u=q.front(); q.pop(); for (int i=head[u];i;i=e[i].next) { int v=e[i].to; if (dep[u]+1<dep[v]&&e[i].c) { dep[v]=dep[u]+1; q.push(v); } } } return (dep[t]<Inf); } int dfs(int u,int low) { int lows=0; if (u==t) return low; //到达汇点 for (int i=head[u];i;i=e[i].next) { int v=e[i].to; if (dep[v]==dep[u]+1&&e[i].c) { lows=dfs(v,min(e[i].c,low)); //求最小边权 if (!lows) continue; //最小边权为0 e[i].c-=lows; //正向边 e[((i+1)^1)-1].c+=lows; //反向边 return lows; } } return 0; } bool print() //输出不解释 { for (int i=n+1;i<=n+m;i++) { printf("%d:",i-n); for (int j=head[i];j;j=e[j].next) { int v=e[j].to; if (v<t&&e[j].c) printf(" %d",v); } putchar(10); } return false; } int main() { scanf("%d%d",&m,&n); s=0; t=n+m+1; for (int i=1;i<=m;i++) { scanf("%d",&x); num+=x; add(n+i,t,x); add(t,n+i,0); } for (int j=1;j<=n;j++) { add(s,j,1); add(j,s,0); scanf("%d",&x); for (int i=1;i<=x;i++) { scanf("%d",&y); add(j,n+y,1); add(n+y,j,0); } } while (bfs()) //只要能分层就循环 { while (sum=dfs(s,INF)) ans+=sum; } if (ans<num) return printf("No Solution!\n")&0; return print(); }