【24题】试题库问题【网络流】
题目大意:
假设一个试题库中有道试题。每道试题都标明了所属类别。同一道题可能有多个类别属性。现要从题库中抽取道题组成试卷。并要求试卷包含指定类型的试题。
思路:
这道题很明显是一个二分图。所以首选。
对于每一个试卷,将它连向汇点,流量为所需的题目总数;而每一道题就连向原点,流量为。而中间则将每一道题连向它可以放入的试卷,流量为。之后跑一边,若则可以组成试卷,否则不行。
代码:
#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();
}