刷題總結——魔術球問題(ssoj最小路徑覆蓋+網絡流)

題目:

題目描述

假設有 n 根柱子,現要按下述規則在這 n 根柱子中依次放入編號爲 1,2 ,3,… 的球。
(1)每次只能在某根柱子的最上面放球。
(2)在同一根柱子中,任何 2 個相鄰球的編號之和爲徹底平方數。
試設計一個算法,計算出在 n 根柱子上最多能放多少個球。例如,在 4 根柱子上最多放 11 個球。
對於給定的 n,計算在 n 根柱子上最多能放多少個球。ios

輸入格式

輸入文件第 1 行有 1 個正整數 n(1<n<60),表示柱子數。算法

輸出格式

輸出 n 根柱子上最多能放的球數。網絡

樣例數據 1

輸入  [複製]post

 

 

4

輸出spa

11

備註

【樣例說明】
最多能放 11 個球,下面 4 行,每行是一根柱子上的球的編號。
1 8
2 7 9
3 6 10
4 5 11設計

【思考如下輸出樣式】
將 n 根柱子上最多能放的球數以及相應的放置方案輸出到文件中。
文件的第一行是球數。接下來的 n 行,每行是一根柱子上的球的編號。rest

題解:

  首先能夠想到這道題的策略確定是向上枚舉球的數量而後判斷····code

  建圖方法是:若是對於i<j有i+j爲一個徹底平方數,鏈接一條有向邊(i,j)。該圖是有向無環圖,求最小路徑覆蓋。若是恰好知足最小路徑覆蓋數等於N,那麼A是一個可行解,在全部可行解中找到最大的A,即爲最優解。最小路徑覆蓋相關知識點以下:blog

  有向無環圖最小不相交路徑覆蓋ip

  定義:用最少的不相交路徑覆蓋全部頂點。

  定理:把原圖中的每一個點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;
}
相關文章
相關標籤/搜索