[网络流24题] 4. 魔术球问题 解题报告

魔术球问题

题意

\(n\) 个柱子 \((4\le n \le 55)\),

往这 \(n\) 个柱子上依次放编号为 $1,2,3,\dots $ 的球,

要求同一个柱子上相邻两球的编号之和为完全平方数,

求最多能放多少个球.

思路

方法一

考虑二分答案, 每次二分球数,

设当前有 \(t\) 个球, 那么这个问题就可以转化为最小路径覆盖问题,

相当于有 \(t\) 个点, 点之间有若干条边, 初始状态可以认为是用 \(t\) 条路径覆盖了这张图, 那么就看哪些路径是可以合并的.

把每个点拆成两个点, 一个代表出度, 一个代表入度,

\(S\) 向代表出度的点连一条容量为 \(1\) 的边, 代表入度的点向 \(T\) 连一条容量为 \(1\) 的边, 点与点之间的边就按照原图连接, 然后跑最大流就行了.


方法二

其实不需要二分答案, 只需要逐个把点加入网络中, 然后在残量网络上跑最大流就行了.

(但是二分还更快一点, 估计是因为逐个加点时, \(Dinic\) 算法的优势表现不出来, 相当于 "退化" 成了 \(EK\))

代码

方法一

#include<bits/stdc++.h>
#define pb push_back
#define sz size
using namespace std;
const int _=6050+7;
const int __=344850+7;
const int inf=0x3f3f3f3f;
int t,n,S,T,d[_],max_flow,result,ans;
int lst[_],nxt[__],to[__],c[__],tot=1;
queue<int> q;
vector<int> plr[55+7];
void add(int x,int y,int cap){
  nxt[++tot]=lst[x];
  to[tot]=y;
  c[tot]=cap;
  lst[x]=tot;
}
void init(){
  tot=1; max_flow=0;
  memset(lst,0,sizeof(lst));
  S=2*n+1; T=2*n+2;
  for(int i=1;i<=n;i++){
    add(S,i,1);
    add(i,S,0);
    add(i+n,T,1);
    add(T,i+n,0);
  }
  for(int i=1;i*i<=2*n;i++)
    for(int j=1;j<=n;j++){
      if(i*i-j<=j) break;
      if(i*i-j>n) continue;
      add(j,i*i-j+n,1);
      add(i*i-j+n,j,0);
    }
}
bool bfs(){
  memset(d,0,sizeof(d));
  while(!q.empty()) q.pop();
  d[S]=1; q.push(S);
  while(!q.empty()){
    int u=q.front(); q.pop();
    for(int i=lst[u];i;i=nxt[i]){
      int v=to[i];
      if(d[v]||!c[i]) continue;
      d[v]=d[u]+1;
      if(v==T) return 1;
      q.push(v);
    }
  }
  return 0;
}
int dfs(int u,int flow){
  if(u==T) return flow;
  int rest=flow;
  for(int i=lst[u];i;i=nxt[i]){
    int v=to[i];
    if(d[v]!=d[u]+1||!c[i]) continue;
    int cost=dfs(v,min(rest,c[i]));
    c[i]-=cost; c[i^1]+=cost;
    rest-=cost;
  }
  return flow-rest;
}
void Dinic(){
  int flow;
  while(bfs()){
    do{
      flow=dfs(S,inf);
      max_flow+=flow;
    }while(flow);
  }
  result=n-max_flow;
}
void save(){
  int cnt=0;
  for(int i=lst[T];i;i=nxt[i]){
    if(c[i]) continue;
    int u=to[i]-n;
    cnt++;
    vector<int>().swap(plr[cnt]);
    while(1){
      int las=u;
      plr[cnt].pb(u);
      for(int i=lst[u];i;i=nxt[i])
	if(to[i]-n<=n&&!c[i]){ u=to[i]-n; break; }
      if(u==las) break;
    }
  }
}
bool judge(int mid){
  n=mid;
  init();
  Dinic();
  if(result<=t){
    if(result==t) save();
    return 1;
  }
  else return 0;
}
void print(){
  printf("%d\n",ans);
  for(int i=1;i<=t;i++){
    for(int j=0;j<(int)plr[i].sz();j++)
      printf("%d ",plr[i][j]);
    putchar('\n');
  }
}
int main(){
#ifndef ONLINE_JUDGE
  freopen("x.in","r",stdin);
#endif
  cin>>t;
  int l=11,r=3025;
  while(l<=r){
    int mid=(l+r)>>1;
    if(judge(mid)){ ans=mid; l=mid+1; }
    else r=mid-1;
  }
  print();
  return 0;
}

方法二

#include<bits/stdc++.h>
#define pb push_back
#define sz size
using namespace std;
const int _=6050+7;
const int __=344850+7;
const int inf=0x3f3f3f3f;
int t,n,m=3025,S=6051,T=6052,d[_],max_flow,ans;
int lst[_],nxt[__],to[__],c[__],tot=1;
queue<int> q;
vector<int> plr[55+7];
void add(int x,int y,int cap){
  nxt[++tot]=lst[x];
  to[tot]=y;
  c[tot]=cap;
  lst[x]=tot;
}
void init(){
  n++;
  add(S,n,1); add(n,S,0);
  add(n+m,T,1); add(T,n+m,0);
  for(int i=1;i*i-n<n;i++){
    int j=i*i-n;
    if(j<0) continue;
    if(j<n){
      add(j,n+m,1);
      add(n+m,j,0);
    }
    else if(j>n){
      add(n,j+m,1);
      add(j+m,n,0);
    }
  }
}
bool bfs(){
  memset(d,0,sizeof(d));
  d[S]=1; q.push(S);
  while(!q.empty()){
    int u=q.front(); q.pop();
    for(int i=lst[u];i;i=nxt[i]){
      int v=to[i];
      if(d[v]||!c[i]) continue;
      d[v]=d[u]+1;
      if(v==T) return 1;
      q.push(v);
    }
  }
  return 0;
}
int dfs(int u,int flow){
  if(u==T) return flow;
  int rest=flow;
  for(int i=lst[u];i;i=nxt[i]){
    int v=to[i];
    if(d[v]!=d[u]+1||!c[i]) continue;
    int cost=dfs(v,min(rest,c[i]));
    c[i]-=cost; c[i^1]+=cost;
    rest-=cost;
  }
  return flow-rest;
}
void Dinic(){
  int flow;
  while(bfs())
    do{
      flow=dfs(S,inf);
      max_flow+=flow;
    }while(flow);
}
void save(){
  int cnt=0;
  for(int k=lst[T];k;k=nxt[k]){
    if(c[k]) continue;
    int u=to[k]-m; cnt++;
    vector<int>().swap(plr[cnt]);
    while(1){
      int las=u;
      plr[cnt].pb(u);
      for(int i=lst[u];i;i=nxt[i])
	if(to[i]!=S&&!c[i]){ u=to[i]-m; break; }
      if(u==las) break;
    }
  }
}
bool idk(){
  init();
  Dinic();
  if(n-max_flow==t) save();
  return n-max_flow<=t;
}
void print(){
  n--;
  printf("%d\n",n);
  for(int i=1;i<=t;i++){
    for(int j=0;j<(int)plr[i].sz();j++)
      printf("%d ",plr[i][j]);
    putchar('\n');
  }
}
int main(){
#ifndef ONLINE_JUDGE
  freopen("x.in","r",stdin);
#endif
  cin>>t;
  while(idk());
  print();
  return 0;
}
posted @ 2020-01-13 08:08  BruceW  阅读(191)  评论(0编辑  收藏  举报