• 洛谷P2763 试题库问题


    题目描述

    问题描述:

    假设一个试题库中有 nn 道试题。每道试题都标明了所属类别。同一道题可能有多个类别属性。现要从题库中抽取 mm 道题组成试卷。并要求试卷包含指定类型的试题。试设计一个满足要求的组卷算法。

    编程任务:

    对于给定的组卷要求,计算满足要求的组卷方案。

    输入格式

    第一行有两个正整数 kk 和 nn。kk 表示题库中试题类型总数,nn 表示题库中试题总数。

    第二行有 kk 个正整数,第 ii 个正整数表示要选出的类型 ii 的题数。这 kk 个数相加就是要选出的总题数 mm。

    接下来的 nn 行给出了题库中每个试题的类型信息。每行的第一个正整数 pp 表明该题可以属于 pp 类,接着的 pp 个数是该题所属的类型号。

    输出格式

    输出共 kk 行,第 ii 行输出 i: 后接类型 ii 的题号。
    如果有多个满足要求的方案,只要输出一个方案。
    如果问题无解,则输出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

    说明/提示

    2\leq k \leq 202≤k≤20,k \leq n \leq 10^3k≤n≤103。


    感谢 @PhoenixEclipse 提供 spj

    只是记录,不多说

    1. #include<iostream>
    2. #include<algorithm>
    3. #include<map>
    4. #include<cmath>
    5. #include<cstring>
    6. #include<iomanip>
    7. #include<numeric>
    8. #include<stack>
    9. #include<queue>
    10. #include<set>
    11. #include<string>
    12. #include<vector>
    13. using namespace std;
    14. typedef long long ll;
    15. typedef unsigned long long ull;
    16. const int inf=0x3f3f3f3f,int_max=0x7fffffff;
    17. //这道题还是最大流,但是就是每个类型的结点的流量是有一个上限的,并且要标记说被选中的是归属于哪一类的
    18. //这个其实也不用标记,就是最后跑一遍深搜看哪些边是 cap==flow 的,是的话就是选中到这一类中了
    19. const int maxn=1100,maxm=maxn*21;
    20. struct edge{
    21. int v,nxt,cap,flow;
    22. edge():flow(0){}
    23. }edg[maxm*2];
    24. int ecnt=0,head[maxn],d[maxn],cur[maxn],n,m,k,s,t;
    25. set<int> ans[25];
    26. bool vis[maxn];
    27. void addedge(int u,int v,int cap)//cap 正边是cap,反边是0
    28. {
    29. edg[ecnt].v=v;
    30. edg[ecnt].nxt=head[u];
    31. edg[ecnt].cap=cap;
    32. head[u]=ecnt++;
    33. }
    34. bool bfs()
    35. {
    36. memset(d,63,sizeof d);
    37. memset(vis,0,sizeof vis);
    38. queue<int> q;
    39. q.push(s);
    40. d[s]=0;
    41. vis[s]=1;
    42. while(!q.empty())
    43. {
    44. int u=q.front();
    45. q.pop();
    46. for(int i=head[u];~i;i=edg[i].nxt)
    47. {
    48. edge &e=edg[i];
    49. if(!vis[e.v]&&e.cap>e.flow)
    50. {
    51. vis[e.v]=1;
    52. d[e.v]=d[u]+1;
    53. q.push(e.v);
    54. }
    55. }
    56. }
    57. return vis[t];
    58. }
    59. ll dfs(int u,ll fi)
    60. {
    61. if(u==t||!fi)return fi;
    62. ll flow=0,fo;
    63. for(int &i=cur[u];~i;i=edg[i].nxt)
    64. {
    65. edge &e=edg[i];
    66. if(d[u]+1==d[e.v]&&(fo=dfs(e.v,min(fi,(ll)e.cap-e.flow)))>0)
    67. {
    68. e.flow+=fo;
    69. edg[i^1].flow-=fo;
    70. flow+=fo;
    71. fi-=fo;
    72. if(!fi)break;
    73. }
    74. }
    75. return flow;
    76. }
    77. ll maxFlow()
    78. {
    79. ll flow=0;
    80. while(bfs())
    81. {
    82. memcpy(cur,head,sizeof cur);
    83. flow+=dfs(s,(ll)inf*inf);
    84. }
    85. return flow;
    86. }
    87. int main()
    88. {
    89. ios_base::sync_with_stdio(0),cin.tie(0);
    90. s=1098,t=1097;//随便给个可以的就行
    91. memset(head,-1,sizeof head);
    92. cin>>k>>n;
    93. for(int i=1;i<=k;i++)
    94. {
    95. int nd;
    96. cin>>nd;
    97. addedge(s,i+1005,nd);
    98. addedge(i+1005,s,0);
    99. }
    100. for(int i=1;i<=n;i++)
    101. {
    102. int num;
    103. cin>>num;
    104. addedge(i,t,1);
    105. addedge(t,i,0);
    106. while(num--)
    107. {
    108. int tp;
    109. cin>>tp;
    110. tp+=1005;
    111. addedge(tp,i,1);
    112. addedge(i,tp,0);
    113. }
    114. }
    115. maxFlow();//md一开始忘记调用这个了,检查了好久
    116. bool flag=1;
    117. for(int i=head[s];~i;i=edg[i].nxt)
    118. {
    119. if(edg[i].cap-edg[i].flow)
    120. {
    121. flag=0;
    122. break;
    123. }
    124. if(!edg[i].cap)continue;
    125. //注意如果没有上面这一步,那么后面在遍历边的时候就会遍历到那个类型到s的一条反向边
    126. //(而正常情况下是不会这样的,这是由于反向边是最先加入的,所以最晚被遍历到,因此不是0的时候就是不会出错的)
    127. //最合理的解决方法应该就是判断要是终点是s的话就去掉
    128. int tp=edg[i].v;
    129. for(int j=head[tp];~j;j=edg[j].nxt)
    130. {
    131. if(edg[j].cap==edg[j].flow)ans[tp-1005].insert(edg[j].v);
    132. }
    133. }
    134. if(!flag)cout<<"No Solution!";
    135. else
    136. {
    137. for(int i=1;i<=k;i++)
    138. {
    139. cout<<i<<": ";
    140. for(auto a:ans[i])cout<<a<<" ";
    141. cout<<"\n";
    142. }
    143. }
    144. return 0;
    145. }

  • 相关阅读:
    429. N 叉树的层序遍历
    【Git】Git的基本概念,Git的基本操作
    被Chatgpt碾压的打工人与大学生,准备反击!
    谷粒学院 —— 10、课程管理:整合阿里云视频点播
    自己动手从零写桌面操作系统GrapeOS系列教程——8.x86介绍
    nodejs环境安装与配置记录
    VC++将资源文件编译进程序并在运行时释放到文件
    【论文阅读】DeepLab:语义图像分割与深度卷积网络,自然卷积,和完全连接的crf
    面试常见问题:如何回答才得体?
    【嵌入式面试题】常见面试题梳理二
  • 原文地址:https://blog.csdn.net/PONY_10001/article/details/126832075