魔术球问题(网络流24题)

问题描述:

假设有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\n",m-1);print();exit(0);}
  }
}
View Code

 还有贪心的方法,有可以放的柱子就放,不然就新开(正确性就不知道了)。

貌似球的数量还有规律2+2+4+4+6+6+....

猜你喜欢

转载自www.cnblogs.com/sto324/p/11334014.html