传送门
这个题好像比较水。
每个种类向汇点连容量为所需求的数量的边
然后每个试题向可以选的种类连容量为1的边
再从源点向每个试题连容量为1的边,然后dinic
过程中记录一下转移的目标节点,然后输出路径就好了
判无解不用我说了吧。
代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstdlib>
#include<vector>
#define ll long long
using namespace std;
inline int read(){
int x=0;char ch=' ';int f=1;
while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
if(ch=='-')f=-1,ch=getchar();
while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return x*f;
}
const int N=2005,M=N*10;
int k,n,sum,tot=-1,s,t;
int head[N],d[N],q[N],cur[N],Nxt[N],Next[M],to[M],flow[M];
inline void addedge(int x,int y,int c){
to[++tot]=y;Next[tot]=head[x];flow[tot]=c;head[x]=tot;
to[++tot]=x;Next[tot]=head[y];flow[tot]=0;head[y]=tot;
}
inline bool bfs(){
for(int i=s;i<=t;i++)d[i]=0x3f3f3f3f;
int l=1,r=1;d[s]=0;q[1]=s;
while(l<=r){
int x=q[l++];
for(int i=head[x];i!=-1;i=Next[i]){
int u=to[i];
if(flow[i]&&d[u]>d[x]+1){
d[u]=d[x]+1;
q[++r]=u;
}
}
}
return d[t]!=0x3f3f3f3f;
}
inline int dfs(int x,int a){
if(x==t||!a)return a;
int F=0,f;
for(int &i=cur[x];i!=-1;i=Next[i]){
int u=to[i];
if(flow[i]&&d[u]==d[x]+1&&(f=dfs(u,min(a,flow[i])))>0){
flow[i]-=f;
flow[i^1]+=f;
F+=f;
a-=f;
Nxt[x]=u;
if(!a)return F;
}
}
return F;
}
inline int dinic(){
int F=0;
while(bfs()){
for(int i=s;i<=t;i++)cur[i]=head[i];
F+=dfs(s,0x3f3f3f3f);
}
return F;
}
vector<int> T[N];
int main(){
memset(head,-1,sizeof(head));
k=read();n=read();s=0;t=n+k+1;
for(int i=1;i<=k;i++){
int v=read();
sum+=v;
addedge(n+i,t,v);
}
for(int i=1;i<=n;i++){
int cnt=read();
addedge(s,i,1);
while(cnt--){
int v=read();
addedge(i,n+v,1);
}
}
int ans=dinic();
if(ans!=sum)printf("No Solution!");
else{
for(int i=1;i<=n;i++){
if(!Nxt[i])continue;
T[Nxt[i]-n].push_back(i);
}
vector<int>::iterator it;
for(int i=1;i<=k;i++){
printf("%d: ",i);
for(it=T[i].begin();it!=T[i].end();++it){
printf("%d ",*it);
}
putchar('\n');
}
}
return 0;
}