传送门
I think
点集x,y分别放置试题与类型。源点向x集点连容量为1的边,x集点向y中其所属类型连容量为1的边,y集点向T连容量为所需量的边,求解最大流若等于总题数则有解。
Code
#include<cstdio>
#include<queue>
#include<vector>
using namespace std;
const int sm = 1100;
const int sn = 22000;
const int Inf = 0x3f3f3f3f;
int N,k,M,S,T,tot=1,Flw; bool fl;
int to[sn],nxt[sn],hd[sm],c[sn];
int lev[sm],cur[sm];
vector<int>Que[sm];
int Min(int x,int y) { return x<y?x:y; }
void Add(int u,int v,int w) {
to[++tot]=v,nxt[tot]=hd[u],hd[u]=tot,c[tot]=w;
to[++tot]=u,nxt[tot]=hd[v],hd[v]=tot,c[tot]=0;
}
bool Bfs() {
for(int i=1;i<=T;++i) lev[i]=0;
int t; queue<int>q;
q.push(S);lev[S]=1;
while(!q.empty()) {
t=q.front(),q.pop();
for(int i=hd[t];i;i=nxt[i])
if(c[i]>0&&!lev[to[i]]) {
lev[to[i]]=lev[t]+1;
if(to[i]==T) return 1;
q.push(to[i]);
}
}
return 0;
}
int Dfs(int x,int mx) {
if(x==T||!mx) return mx;
int f;
for(int i=cur[x]?cur[x]:hd[x];i;i=nxt[i]) {
cur[x]=i;
if(c[i]>0&&lev[to[i]]==lev[x]+1)
if(f=Dfs(to[i],Min(mx,c[i])))
return c[i]-=f,c[i^1]+=f,f;
}
return 0;
}
void Dinic() {
int f;
while(Bfs()) {
for(int i=1;i<=T;++i) cur[i]=0;
while(f=Dfs(S,Inf)) Flw+=f;
}
}
int main() {
int u,v;
scanf("%d%d",&k,&N);
S=k+N+1,T=S+1;
for(int i=1;i<=k;++i) {
scanf("%d",&u);
Add(N+i,T,u),M+=u;
}
for(int i=1;i<=N;++i) {
scanf("%d",&v);
Add(S,i,1);
for(int j=1;j<=v;++j)
scanf("%d",&u),Add(i,u+N,1);
}
Dinic();
if(Flw<M) puts("No Solution!");
else {
for(int i=1;i<=N;++i)
for(int j=hd[i];j;j=nxt[j])
if(to[j]!=S&&!c[j]) {
Que[to[j]-N].push_back(i);
break;
}
for(int i=1;i<=k;++i) {
printf("%d:",i);
for(int j=0;j<Que[i].size();++j)
printf("%d ",Que[i][j]);
putchar(10);
}
}
return 0;
}