网络流24题-4
来源:互联网 发布:织梦cms百科 编辑:程序博客网 时间:2024/06/13 18:36
魔术球问题
«问题描述:
假设有 n 根柱子,现要按下述规则在这 n 根柱子中依次放入编号为 1,2,3,…….的球。
(1)每次只能在某根柱子的最上面放球。
(2)在同一根柱子中,任何 2 个相邻球的编号之和为完全平方数。
试设计一个算法,计算出在 n 根柱子上最多能放多少个球。例如,在 4 根柱子上最多可
放 11 个球。
«编程任务:
对于给定的 n,计算在 n 根柱子上最多能放多少个球。
«数据输入:
由文件 input.txt 提供输入数据。文件第 1 行有 1 个正整数 n,表示柱子数。
«结果输出:
程序运行结束时,将 n 根柱子上最多能放的球数以及相应的放置方案输出到文件
output.txt 中。文件的第一行是球数。接下来的 n 行,每行是一根柱子上的球的编号。
输入文件示例
input.txt
4
输出文件示例
output.txt
11
1 8
2 7 9
3 6 10
4 5 11
【问题分析】
枚举答案转化为判定性问题,然后最小路径覆盖,可以转化成二分图最大匹配,从而用最大流解决。
【建模方法】
枚举答案A,在图中建立节点1..A。如果对于i < j有i+j为一个完全平方数,连接一条有向边(i,j)。该图是有向无环图,求最小路径覆盖。如果刚好满足最小路径覆盖数等于N,那么A是一个可行解,在所有可行解中找到最大的A,即为最优解。
具体方法可以顺序枚举A的值,当最小路径覆盖数刚好大于N时终止,A-1就是最优解。
【建模分析】
由于是顺序放球,每根柱子上的球满足这样的特征,即下面的球编号小于上面球的编号。抽象成图论,把每个球看作一个顶点,就是编号较小的顶点向编号较大的顶点连接边,条件是两个球可以相邻,即编号之和为完全平方数。每根柱子看做一条路径,N根柱子要覆盖掉所有点,一个解就是一个路径覆盖。
最小路径覆盖数随球的数量递增不递减,满足单调性,所以可以枚举答案(或二分答案),对于特定的答案求出最小路径覆盖数,一个可行解就是最小路径覆盖数等于N的答案,求出最大的可行解就是最优解。本问题更适合枚举答案而不是二分答案,因为如果顺序枚举答案,每次只需要在残量网络上增加新的节点和边,再增广一次即可。如果二分答案,就需要每次重新建图,大大增加了时间复杂度。
—byvoid
个人理解:最多能放多少不知道,所以需要枚举答案,但答案具有单调性质,能放N个绝对能放 < N个。二分答案。顺序枚举也可以。建图,1~N,如果有i < j(i+j为完全平方数链接)如果刚好满足就是可以,最小路径覆盖数为N,一个柱子起代表的就是一个路径。但二分答案无法利用上次的残余网络结果所以枚举更快一点。
#include<iostream>#include<math.h>#include<cstring>#include<algorithm>#include<set>#include<map>#include<queue>#include<cstdio>#include<vector>const int INF=0x7fffffff;using namespace std;const int maxn=4001;struct edge{ int to,cap,rev;};vector<edge> G[maxn];int level[maxn],match[maxn],path[maxn],iter[maxn];bool book[maxn],issquare[maxn];int N,s=0,OFFSET=2000,t=maxn-1,Ans,maxflow;void add_edge(int from,int to,int cap){ G[from].push_back((edge){to,cap,G[to].size()}); G[to].push_back((edge){from,0,G[from].size()-1});}void init(){ cin>>N; for(int i=1;i<=60;i++) issquare[i*i]=true;}void bfs(int s){ memset(level,-1,sizeof(level)); queue<int > que; level[s]=0; que.push(s); while(!que.empty()) { int v=que.front(); que.pop(); for(int i=0;i<G[v].size();i++) { edge &e=G[v][i]; if(e.cap&&level[e.to]<0) { level[e.to]=level[v]+1; que.push(e.to); } } }}int dfs(int v,int t,int f){ if(v==t) return f; for(int &i=iter[v];i<G[v].size();i++) { edge &e=G[v][i]; if(e.cap&&level[e.to]>level[v]) { int d=dfs(e.to,t,min(e.cap,f)); if(d>0) { e.cap-=d; G[e.to][e.rev].cap+=d; return d; } } } return 0;}int max_flow(int s,int t){ int flow=0; for(;;) { bfs(s); if(level[t]<0) return flow; memset(iter,0,sizeof(iter)); int f=0; while((f=dfs(s,t,INF))>0) flow+=f; }}void solve(){ int i,j; add_edge(s,1,1); add_edge(1+OFFSET,t,1); maxflow+=max_flow(s,t); for(i=1;i-maxflow<=N;) { i++; add_edge(s,i,1); add_edge(i+OFFSET,t,1); for(j=1;j<i;j++) if(issquare[i+j]) add_edge(j,i+OFFSET,1); maxflow+=max_flow(s,t); } Ans=i-1; for(int i=0;i<=Ans;i++) G[i].clear(); G[t].clear(); for(int i=1;i<=Ans;i++) { add_edge(s,i,1); add_edge(i+OFFSET,t,1); for(int j=1;j<i;j++) if(issquare[i+j]) add_edge(j,i+OFFSET,1); } maxflow=max_flow(s,t);}void print(){ cout<<Ans<<endl; int i,j,p; for(i=Ans+OFFSET;i>OFFSET;i--) { for(j=0;j<G[i].size();j++) { edge &e=G[i][j]; if(e.cap==1) { match[e.to]=i-OFFSET; break; } } } memset(book,false,sizeof(book)); for(i=1;i<=Ans;i++) { p=0; if(book[i]) continue; for(j=i;j;j=match[j]) { path[++p]=j; book[j]=true; } for(j=1;j<p;j++) cout<<path[j]<<" "; cout<<path[j]<<endl; }}int main(){ init(); solve(); print(); return 0;}
- 网络流24题-4
- 【网络流】网络流24题
- [网络流]: 网络流24题
- 网络流24题
- 网络流24题
- 网络流24题
- 网络流24题
- 网络流24题---餐巾纸
- [网络流24题] 餐巾
- 网络流24题-2
- 网络流24题-3
- 网络流24题-5
- 网络流24题-6
- 网络流24题-7
- 网络流24题-8
- 网络流24题-9
- 网络流24题-11
- 网络流24题-12
- 第三课 Tensorflow实现人工神经网络
- ConfigParser模块详解
- Qt5.9.1+mingw32静态编译
- spoj COT
- BZOJ 1007 几何 解题报告
- 网络流24题-4
- 最短路算法
- Docker私有仓库搭建
- SpringAop--切面实例
- 递推和递归一
- 剑指offer:数组中重复的数字
- 用两个栈实现队列java实现
- yum 和 rpm安装mysql彻底删除
- JAVA对象的创建过程