Libre 6003 「网络流 24 题」魔术球 (网络流,最大流)

Description

假设有n根柱子,现要按下述规则在这n根柱子中依次放入编号为 1,2,3,4......的球。

(1)每次只能在某根柱子的最上面放球。

(2)在同一根柱子中,任何2个相邻球的编号之和为完全平方数。

试设计一个算法,计算出在n根柱子上最多能放多少个球。例如,在4 根柱子上最多可 放11个球。

´编程任务:

对于给定的n,计算在 n根柱子上最多能放多少个球。

Input

第1 行有 1个正整数n,表示柱子数。

Output

第一行是球数。接下来的 n行,每行是一根柱子上的球的编号。

Sample Input

4

Sample Output

11

1 8

2 7 9

3 6 10

4 5 11

Http

Libre:https://loj.ac/problem/6003

Source

网络流

解决思路

这道题不好想到是网络流的题目。

博主觉得自己的解释有些问题,等博主想清楚了再写吧。。。(2018.1.2 Update)

我们从小到大依次枚举最大能放入的球的大小。对于每一个球,我们将其拆成两个,一个入点一个出点。从源点向入点连一条流量为1的边,从出点向汇点也连一条流量为1的边,这样是为了保证一个球要么上面或下面没有球要么每一面至多有一个球。当我们在枚举球x时,从1到x-1循环看一看有那些球满足它与x的和是完全平方数。如果是,则在那个球的出点与x的入点之间连一条流量为1的边,同样是为了保证只有一个球。

通过上面这样的模型转换,我们就把题目变成了求最少路径覆盖。关于最少路径覆盖,可以到我的这一篇文章查看

然后我们就可以每一次加入一个数即其相应的边,跑一边最大流求出最小路径覆盖,如果当前的路径覆盖数大于我们输入的柱子数,则说明答案是当前-1.

需要注意的是因为我们是逐渐加边的,即每一次其实是在上一次的残量网络上加边,所以统计路径覆盖数时不要重新统计,而是在原来的基础上进行统计。

至于如何输出一组方案呢?我们把图全部重新建一遍,再跑最大流,然后从小到大扫描每一个数,若发现该数还未访问,则从该数出发,寻找它的流流向了哪个点,循环输出,直到某个点的出点是到汇点,把这条路径上的点全部打上标记。这样对比输出即可。

另:这里使用Dinic实现最大流,关于Dinic算法,请移步我的这篇文章

代码

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstdlib>
  4. #include<cstring>
  5. #include<algorithm>
  6. #include<cmath>
  7. using namespace std;
  8. const int maxN=5000;
  9. const int maxM=100001;
  10. const int inf=2147483647;
  11. class Edge
  12. {
  13. public:
  14. int u,v,flow;
  15. };
  16. int n,m;
  17. int cnt=-1;
  18. int Head[maxN];
  19. int Next[maxM];
  20. Edge E[maxM];
  21. int depth[maxN];
  22. int cur[maxN];
  23. int Q[maxM];
  24. bool vis[maxN];
  25. void Add_Edge(int u,int v,int flow);
  26. bool bfs();
  27. int dfs(int u,int flow);
  28. int main()//笔者这里用0代表源点,1代表汇点;而对于每一个点u,其拆点后的入点为u*2,出点为u*2+1
  29. {
  30. int sum;
  31. memset(Head,-1,sizeof(Head));
  32. scanf("%d",&sum);
  33. int Ans=0;
  34. n=0;
  35. do//从1开始加点和加边
  36. {
  37. n++;
  38. Ans++;//注意这里,每一次开始时,加入一个数相当于要多一条路径,因为我们还没有把其放入任何以前的路径中
  39. Add_Edge(0,n*2,1);//先连上n与汇点和n与源点
  40. Add_Edge(n*2+1,1,1);
  41. for (int i=1;i<n;i++)//循环扫描以前的数,若满足和为完全平方数,则连边
  42. if (sqrt(n+i)==(int)(sqrt(n+i)))
  43. Add_Edge(i*2,n*2+1,1);
  44. while (bfs())
  45. {
  46. for (int i=0;i<=n*2+1;i++)
  47. cur[i]=Head[i];
  48. while (int di=dfs(0,inf))
  49. Ans-=di;//注意这Ans是减,因为当增广出一条新的路径时,说明原路径数减1
  50. }
  51. if (Ans>sum)//若发现此时路径数已经比柱子数大了,则说明前一个是答案,退出并减一
  52. {
  53. n--;
  54. break;
  55. }
  56. }
  57. while (1);
  58. cout<<n<<endl;
  59. for (int i=0;i<=cnt;i++)//重新建图
  60. if ((E[i].u<E[i].v)||(E[i].v==1))
  61. E[i].flow=1;
  62. else
  63. E[i].flow=0;
  64. Ans=0;//再跑一边最大流
  65. while (bfs())
  66. {
  67. for (int i=0;i<=n*2+1;i++)
  68. cur[i]=Head[i];
  69. while (int di=dfs(0,inf))
  70. Ans+=di;
  71. }
  72. memset(vis,0,sizeof(vis));//输出方案
  73. for (int i=1;i<=n;i++)
  74. {
  75. if (vis[i]==1)
  76. continue;
  77. int now=i;
  78. bool get=0;
  79. do
  80. {
  81. cout<<now<<' ';
  82. vis[now]=1;
  83. get=0;
  84. for (int i=Head[now*2];i!=-1;i=Next[i])
  85. if ((E[i].v%2!=0)&&(E[i].flow==0))
  86. {
  87. get=1;
  88. now=E[i].v/2;
  89. break;
  90. }
  91. }
  92. while (get==1);
  93. cout<<endl;
  94. }
  95. return 0;
  96. }
  97. void Add_Edge(int u,int v,int flow)
  98. {
  99. cnt++;
  100. Next[cnt]=Head[u];
  101. Head[u]=cnt;
  102. E[cnt].u=u;
  103. E[cnt].v=v;
  104. E[cnt].flow=flow;
  105. cnt++;
  106. Next[cnt]=Head[v];
  107. Head[v]=cnt;
  108. E[cnt].u=v;
  109. E[cnt].v=u;
  110. E[cnt].flow=0;
  111. }
  112. bool bfs()
  113. {
  114. memset(depth,-1,sizeof(depth));
  115. int h=1,t=0;
  116. Q[1]=0;
  117. depth[0]=1;
  118. do
  119. {
  120. t++;
  121. int u=Q[t];
  122. for (int i=Head[u];i!=-1;i=Next[i])
  123. {
  124. int v=E[i].v;
  125. if ((depth[v]==-1)&&(E[i].flow>0))
  126. {
  127. depth[v]=depth[u]+1;
  128. h++;
  129. Q[h]=v;
  130. }
  131. }
  132. }
  133. while (h!=t);
  134. if (depth[1]==-1)
  135. return 0;
  136. return 1;
  137. }
  138. int dfs(int u,int flow)
  139. {
  140. if (u==1)
  141. return flow;
  142. for (int &i=cur[u];i!=-1;i=Next[i])
  143. {
  144. int v=E[i].v;
  145. if ((depth[v]==depth[u]+1)&&(E[i].flow>0))
  146. {
  147. int di=dfs(v,min(flow,E[i].flow));
  148. if (di>0)
  149. {
  150. E[i].flow-=di;
  151. E[i^1].flow+=di;
  152. return di;
  153. }
  154. }
  155. }
  156. return 0;
  157. }

Libre 6003 「网络流 24 题」魔术球 (网络流,最大流)的更多相关文章

  1. 2018.10.14 loj#6003. 「网络流 24 题」魔术球(最大流)

    传送门 网络流好题. 这道题可以动态建图. 不难想到把每个球iii都拆点成i1i_1i1​和i2i_2i2​,每次连边(s,i1),(i2,t)(s,i_1),(i_2,t)(s,i1​),(i2​, ...

  2. LibreOJ 6003. 「网络流 24 题」魔术球 贪心或者最小路径覆盖

    6003. 「网络流 24 题」魔术球 内存限制:256 MiB时间限制:1000 ms标准输入输出 题目类型:传统评测方式:Special Judge 上传者: 匿名 提交提交记录统计讨论测试数据 ...

  3. [loj #6003]「网络流 24 题」魔术球 二分图最小路径覆盖,网络流

    #6003. 「网络流 24 题」魔术球 内存限制:256 MiB时间限制:1000 ms标准输入输出 题目类型:传统评测方式:Special Judge 上传者: 匿名 提交提交记录统计讨论测试数据 ...

  4. LOJ6003 - 「网络流 24 题」魔术球

    原题链接 Description 假设有根柱子,现要按下述规则在这根柱子中依次放入编号为的球. 每次只能在某根柱子的最上面放球. 在同一根柱子中,任何2个相邻球的编号之和为完全平方数. 试设计一个算法 ...

  5. 【刷题】LOJ 6003 「网络流 24 题」魔术球

    题目描述 假设有 \(n\) 根柱子,现要按下述规则在这 \(n\) 根柱子中依次放入编号为 \(1, 2, 3, 4, \cdots\) 的球. 每次只能在某根柱子的最上面放球. 在同一根柱子中,任 ...

  6. [cogs396] [网络流24题#4] 魔术球 [网络流,最大流,最小路径覆盖]

    本题枚举每多一个球需要多少个柱子,可以边加边边计算,每次只需要判断$i-Dinic()$即可:特别注意边界. #include <iostream> #include <algori ...

  7. Libre 6012 「网络流 24 题」分配问题 (网络流,费用流)

    Libre 6012 「网络流 24 题」分配问题 (网络流,费用流) Description 有n件工作要分配给n个人做.第i个人做第j件工作产生的效益为\(c_{ij}\).试设计一个将n件工作分 ...

  8. Libre 6011 「网络流 24 题」运输问题 (网络流,最小费用最大流)

    Libre 6011 「网络流 24 题」运输问题 (网络流,最小费用最大流) Description W 公司有m个仓库和n个零售商店.第i个仓库有\(a_i\)个单位的货物:第j个零售商店需要\( ...

  9. LIbreOJ #6011. 「网络流 24 题」运输问题 最小费用最大流

    #6011. 「网络流 24 题」运输问题 内存限制:256 MiB时间限制:1000 ms标准输入输出 题目类型:传统评测方式:文本比较 上传者: 匿名 提交提交记录统计讨论测试数据   题目描述 ...

随机推荐

  1. Vultr搭建SS服务

    购买VPS VPS又叫虚拟服务器,相当于是讲物理服务器的资源进行虚拟划分然后分配给不同的用户使用. Vultr服务器按小时计费,最低0.004美元/h,算起来2.5美元/月,且destory掉服务器是 ...

  2. PostgreSQL基础知识与基本操作索引页

    磨砺技术珠矶,践行数据之道,追求卓越价值 返回顶级页:PostgreSQL索引页 luckyjackgao@gmail.com 本页记录所有本人所写的PostgreSQL的基础知识和基本操作相关文摘和 ...

  3. RHEL6 最小化系统 编译安装部署zabbix (mysql)

    RHEL6 最小化系统 编译安装部署zabbix (mysql)官方说明详细见:https://www.zabbix.com/documentation/4.0/manual/installation ...

  4. Linux每天一个命令:cat

    Linux cat命令 命令:cat cat 命令用于连接文件并打印到标准输出设备上. 使用权限 所有使用者 语法格式 cat [-AbeEnstTuv] [--help] [--version] f ...

  5. Wannafly挑战赛26-F-msc的棋盘[最小割转化dp]

    题意 一个大小为 \(n*m\) 的棋盘,知道每一列放了多少棋子,求有多少摆放方案满足要求. \(n,m\leq 50\) . 分析 如果是求是否有方案的话可以考虑网络流,行列连边,列容量为 \(b_ ...

  6. CSS快速入门-组合选择器

    <div class="gradefather"> hello1 <div class="father">hello2 <p cl ...

  7. jersey2 整合 spring + hibernate + log4j2

    整合 spring jersey2 官方还未正式支持 spring4, 但网上有好多支持方案,折腾了一圈后,还是用了 spring3; pom 添加以下依赖配置 <!-- Spring --&g ...

  8. PAT甲题题解-1021. Deepest Root (25)-dfs+并查集

    dfs求最大层数并查集求连通个数 #include <iostream> #include <cstdio> #include <algorithm> #inclu ...

  9. 数学口袋精灵app(小学生四则运算app)开发需求

    数学口袋精灵APP,摒除了传统乏味无趣学习数学四则运算的模式,采用游戏的形式,让小朋友在游戏中学习,培养了小朋友对数学的兴趣,让小朋友在游戏中运算能力得到充分提升.快乐学习,成长没烦恼! 项目名字:“ ...

  10. canvas实现五子棋界面

    1.获取canvas画布 var canvas = document.getElementById('canvas'); var context = canvas.getContext('2d'); ...