题目:https://www.luogu.org/problemnew/show/P2763
题目描述
«问题描述:
假设一个试题库中有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!”。
输入输出样例
输入样例#1:
3 15 3 3 4 2 1 2 1 3 1 3 1 3 1 3 3 1 2 3 2 2 3 2 1 3 1 2 1 2 2 1 2 2 1 3 2 1 2 1 1 3 1 2 3
输出样例#1:
1: 1 6 8 2: 7 9 10 3: 2 3 4 5
说明
感谢 @PhoenixEclipse 提供spj
解析
网络流24题之一orz。
二分图多重匹配。
每道题跟T连容量为1的边。
每类题跟S连容量为需要该类题数量的边。
每道题跟它所在的题的类别连容量为1的边。
跑最大流,如果结果小于题目总数,则无解。
否则,对每条连接题的类别和题目的边检查,
若满流,输出题目的编号即可。
至于洛谷的90分:
检查一下是否有条边连了0节点(似乎数据有问题)。
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstring> 5 #include<cmath> 6 #include<vector> 7 #include<queue> 8 using namespace std; 9 #define maxn 10000 10 struct line{ 11 int to; 12 int cap,flow; 13 }; 14 vector<line> edge; 15 vector<int> G[maxn]; 16 bool vis[maxn]; 17 int dis[maxn]; 18 int cur[maxn]; 19 int n,k,m,rep,re; 20 int inv[maxn]; 21 int S,T; 22 void addedge(int from,int to,int cap){ 23 edge.push_back((line){to,cap,0}); 24 edge.push_back((line){from,0,0}); 25 int sz=edge.size(); 26 G[from].push_back(sz-2); 27 G[to].push_back(sz-1); 28 } 29 bool bfs(){ 30 memset(vis,false,sizeof(vis)); 31 vis[S]=true; 32 dis[S]=0; 33 queue<int> q; 34 q.push(S); 35 while (!q.empty()){ 36 int u=q.front(); 37 q.pop(); 38 for (int i=0;i<G[u].size();++i){ 39 line e=edge[G[u][i]]; 40 if (vis[e.to]) continue; 41 if (e.flow>=e.cap) continue; 42 vis[e.to]=true; 43 dis[e.to]=dis[u]+1; 44 q.push(e.to); 45 } 46 } 47 return vis[T]; 48 } 49 int dfs(int x,int a){ 50 if (a==0||x==T) return a; 51 int f,flow=0; 52 for (int &i=cur[x];i<G[x].size();++i){ 53 line &e=edge[G[x][i]]; 54 if (dis[e.to]==dis[x]+1&&(f=dfs(e.to,min(a,e.cap-e.flow)))){ 55 flow+=f; 56 a-=f; 57 e.flow+=f; 58 edge[G[x][i]^1].flow-=f; 59 if (!a) break; 60 } 61 } 62 return flow; 63 } 64 int dinic(){ 65 int ans=0; 66 while (bfs()){ 67 memset(cur,0,sizeof(cur)); 68 ans+=dfs(S,10000); 69 } 70 return ans; 71 } 72 int main(){ 73 scanf("%d%d",&k,&n); 74 S=0; T=2*n+1; 75 for (int i=1;i<=n;++i){ 76 addedge(i+k,T,1); 77 } 78 for (int i=1;i<=k;++i){ 79 scanf("%d",&inv[i]); 80 m+=inv[i]; 81 addedge(S,i,inv[i]); 82 } 83 for (int i=1;i<=n;++i){ 84 scanf("%d",&rep); 85 for (int j=1;j<=rep;++j){ 86 scanf("%d",&re); 87 addedge(re,i+k,1); 88 } 89 } 90 if (dinic()<m){ 91 printf("No Solution!"); 92 return 0; 93 } 94 for (int i=1;i<=k;++i){ 95 printf("%d:",i); 96 for (int j=0;j<G[i].size();++j){ 97 line e=edge[G[i][j]]; 98 if (e.cap==e.flow&&e.to) printf(" %d",e.to-k); 99 } 100 printf("\n"); 101 } 102 return 0; 103 }
AC
90分,100分分别是这么写的:
1 //90分: 2 for (int i=1;i<=k;++i){ 3 printf("%d:",i); 4 for (int j=0;j<G[i].size();++j){ 5 line e=edge[G[i][j]]; 6 if (e.cap==e.flow&&e.to!=k) printf(" %d",e.to-k); 7 } 8 printf("\n"); 9 } 10 //100分: 11 for (int i=1;i<=k;++i){ 12 printf("%d:",i); 13 for (int j=0;j<G[i].size();++j){ 14 line e=edge[G[i][j]]; 15 if (e.cap==e.flow&&e.to) printf(" %d",e.to-k); 16 } 17 printf("\n"); 18 }
compare
希望有大佬给更好解释orz。
时间: 2024-11-12 01:17:50