问题描述:
假设有n根柱子,现要按下述规则在这n根柱子中依次放入编号为1,2,3,...的球。
(1)每次只能在某根柱子的最上面放球。
(2)在同一根柱子中,任何2个相邻球的编号之和为完全平方数。
试设计一个算法,计算出在n根柱子上最多能放多少个球。例如,在4 根柱子上最多可放11 个球。
«编程任务:
对于给定的n,计算在n根柱子上最多能放多少个球。
4<=n<=55
题解
考虑到当球的数量增加时柱子数量不严格单增,所以可以一个一个加球判定需要几个柱子装得下。
把每个球拆成两个点,互成平方数的两个球左右连边,为了确保只连一次从大的向小的连边。
那么问题就变成了最小路径覆盖,路径数就是柱子数。
枚举球为m个,当路径数大于柱子数时答案就是m-1,至于输出方案就从1遍历路径就好了。
为了不超时,我们新加一个球,就从这个球跑一次增广路即可,这也是为什么从大的往小的连边。
#include<bits/stdc++.h> using namespace std; const int maxn=3605; int n,m; int vis[maxn],match[maxn],timer; bool square[maxn<<1]; vector<int> e[maxn]; void init(){ timer=0; memset(match,0,sizeof(match)); memset(vis,0,sizeof(vis)); } bool dfs(int u){ if(vis[u]==timer) return false; vis[u]=timer; for(unsigned int i=0;i<e[u].size();i++){ int v=e[u][i]; if(!match[v]||dfs(match[v])){ match[v]=u; return true; } } return false; } void get(int u){ printf("%d ",u); vis[u]=timer; if(!match[u+1800]){putchar(10);return ;} get(match[u+1800]); } void print(){ ++timer; for(int i=1;i<m;i++) if(vis[i]!=timer) get(i); } int main(){ for(int i=1;i*i<=7200;i++) square[i*i]=true; scanf("%d",&n); int ans=0; while(++m){ for(int i=1;i<m;i++) if(square[i+m]) e[m].push_back(i+1800); ++timer; if(dfs(m)) ans++; if(m-ans>n) {printf("%d ",m-1);print();exit(0);} } }
还有贪心的方法,有可以放的柱子就放,不然就新开(正确性就不知道了)。
貌似球的数量还有规律2+2+4+4+6+6+....