刷题总结——魔术球问题(ssoj最小路径覆盖+网络流)

题目:

题目描述

假设有 n 根柱子,现要按下述规则在这 n 根柱子中依次放入编号为 1,2 ,3,… 的球。
(1)每次只能在某根柱子的最上面放球。
(2)在同一根柱子中,任何 2 个相邻球的编号之和为完全平方数。
试设计一个算法,计算出在 n 根柱子上最多能放多少个球。例如,在 4 根柱子上最多放 11 个球。
对于给定的 n,计算在 n 根柱子上最多能放多少个球。

输入格式

输入文件第 1 行有 1 个正整数 n(1<n<60),表示柱子数。

输出格式

输出 n 根柱子上最多能放的球数。

样例数据 1

输入  [复制]

 

 

4

输出

11

备注

【样例说明】
最多能放 11 个球,下面 4 行,每行是一根柱子上的球的编号。
1 8
2 7 9
3 6 10
4 5 11

【思考以下输出样式】
将 n 根柱子上最多能放的球数以及相应的放置方案输出到文件中。
文件的第一行是球数。接下来的 n 行,每行是一根柱子上的球的编号。

题解:

  首先可以想到这道题的策略肯定是向上枚举球的数量然后判断····

  建图方法是:如果对于i<j有i+j为一个完全平方数,连接一条有向边(i,j)。该图是有向无环图,求最小路径覆盖。如果刚好满足最小路径覆盖数等于N,那么A是一个可行解,在所有可行解中找到最大的A,即为最优解。最小路径覆盖相关知识点如下:

  有向无环图最小不相交路径覆盖

  定义:用最少的不相交路径覆盖所有顶点。

  定理:把原图中的每个点V拆成Vx和Vy,如果有一条有向边A->B,那么就加边Ax-By。这样就得到了一个二分图,最小路径覆盖=原图的节点数-新图最大匹配。

  简单证明:一开始每个点都独立的为一条路径,总共有n条不相交路径。我们每次在二分图里加一条边就相当于把两条路径合成了一条路径,因为路径之间不能有公共点,所以加的边之间也不能有公共点,这就是匹配的定义。所以有:最小路径覆盖=原图的节点数-新图最大匹配。

  因此每次枚举新的点加直接和之前的点枚加边即可···令外每次不用重新在图上跑网络流,记录一个group表示柱子数,和枚举的点数一起加减,然后用group减去新跑的流即可,这样就相当于枚举的点数减去在新图上完全新跑出的流(看不懂的看代码就可以了),即为最小路径覆盖

代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<ctime>
#include<cctype>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
const int inf=1e+9;
const int N=100005;
int src=0,des=10000;
int group,num,n;
int first[N],next[N*2],go[N*2],rest[N*2],tot=1,lev[N],cur[N];
inline void comb(int a,int b,int c)
{
  next[++tot]=first[a],first[a]=tot,go[tot]=b,rest[tot]=c;
  next[++tot]=first[b],first[b]=tot,go[tot]=a,rest[tot]=0;
}
inline bool bfs()
{
  for(int i=src;i<=des;i++)  cur[i]=first[i],lev[i]=-1;
  static int que[N],tail,u,v;
  que[tail=1]=src;
  lev[src]=0;
  for(int head=1;head<=tail;head++)
  {
    u=que[head];
    for(int e=first[u];e;e=next[e])
    {
      if(lev[v=go[e]]==-1&&rest[e])
      {
        lev[v]=lev[u]+1;
        que[++tail]=v;
        if(v==des)  return true;
      }
    }
  }
  return false;
}
inline int dinic(int u,int flow)
{
  if(u==des)
    return flow;
  int res=0,delta,v;
  for(int &e=cur[u];e;e=next[e])
  {
    if(lev[v=go[e]]>lev[u]&&rest[e])
    {
      delta=dinic(v,min(flow-res,rest[e]));
      if(delta)
      {
        rest[e]-=delta;
        rest[e^1]+=delta;
        res+=delta;
        if(res==flow)  break;
      }
    }
  }
  if(flow!=res)  lev[u]=-1;
  return res;
}
inline void maxflow()
{
  while(bfs())
    group-=dinic(src,inf);
}
int main()
{
  //freopen("a.in","r",stdin);
  scanf("%d",&n);
  while(true)
  {
    group++,num++;
    for(int i=1;i<num;i++)
      if(sqrt(i+num)==(int)sqrt(i+num))
        comb(i,num+5000,1);
    comb(num+5000,des,1);
    comb(src,num,1); 
    maxflow();
    if(group>n)  break;
  }
  cout<<num-1<<endl;
  return 0;
}

 

posted @ 2017-09-01 21:03  AseanA  阅读(289)  评论(0编辑  收藏  举报