[网络流24题]魔术球问题

题目描述

«问题描述:

假设有n根柱子,现要按下述规则在这n根柱子中依次放入编号为1,2,3,...的球。

(1)每次只能在某根柱子的最上面放球。

(2)在同一根柱子中,任何2个相邻球的编号之和为完全平方数。

试设计一个算法,计算出在n根柱子上最多能放多少个球。例如,在4 根柱子上最多可放11 个球。

«编程任务:

对于给定的n,计算在n根柱子上最多能放多少个球。

输入输出格式

输入格式:

 

第1 行有1个正整数n,表示柱子数。

 

输出格式:

 

程序运行结束时,将n 根柱子上最多能放的球数以及相应的放置方案输出。文件的第一行是球数。接下来的n行,每行是一根柱子上的球的编号。

题解

可以确定的是一定要边枚举当前要放的球边跑最大流

两个加起来是完全平方的球之间一定是要连边的。

由于放的顺序从小到大,所以连边的方向一定是从当前球向已放球

边的容量均为1,每次在原网络上建边跑最大流,

如果存在最大流,证明当前球可以放在另一球上面

否则就要自立门户了。。。

 

 

如果你单纯地按上面的描述建边,会发现出现了一点小问题。。。

这时候需要用到一个小技巧————拆点

将一个点拆成入点和出点,入点和s,出点和t连边,入点向出点连边

完成!

  1 #include<cmath>
  2 #include<queue>
  3 #include<cstdio>
  4 #include<cstring>
  5 #include<algorithm>
  6 using namespace std;
  7 int n,m,cnt,tot,tp;
  8 int st,ed,ans,num;
  9 int head[200005];
 10 int cur[200005];
 11 int dis[200005];
 12 int pre[200005];
 13 bool usd[200005];
 14 int pr[200005];
 15 struct Edge{
 16     int fr;
 17     int to;
 18     int flw;
 19     int nxt;
 20 }edge[8888888];
 21 void init(){
 22     memset(head,-1,sizeof(head));
 23 }
 24 void addedge(int u,int v,int f){
 25     edge[cnt].fr=u;
 26     edge[cnt].to=v;
 27     edge[cnt].flw=f;
 28     edge[cnt].nxt=head[u];
 29     head[u]=cnt++;
 30     edge[cnt].fr=v;
 31     edge[cnt].to=u;
 32     edge[cnt].nxt=head[v];
 33     head[v]=cnt++;
 34 }
 35 int bfs(int ban){
 36     queue<int>que;
 37     memset(dis,0x3f,sizeof(dis));
 38     que.push(st);dis[st]=1;
 39     while(!que.empty()){
 40         int u=que.front();
 41         que.pop();
 42         for(int i=head[u];i!=-1;i=edge[i].nxt){
 43             int v=edge[i].to;
 44             if(ban==v)continue;
 45             if(!edge[i].flw)continue;
 46             if(dis[v]==0x3f3f3f3f){
 47                 dis[v]=dis[u]+1;
 48                 que.push(v);
 49             }
 50         }
 51     }
 52     return dis[ed]!=0x3f3f3f3f;
 53 }
 54 int dfs(int u,int flw){
 55     if(u==ed)return flw;
 56     int All=0,tmp;
 57     for(int i=cur[u];i!=-1;i=edge[i].nxt){
 58         if(!edge[i].flw)continue;
 59         int v=edge[i].to;cur[u]=i;
 60         if(dis[v]!=dis[u]+1)continue;
 61         if((tmp=dfs(v,min(flw,edge[i].flw)))>0){
 62             pre[v]=u;
 63             edge[i].flw-=tmp;
 64             edge[i^1].flw+=tmp;
 65             flw-=tmp,All+=tmp;
 66             if(!flw)break;
 67         }
 68     }
 69     return All;
 70 }
 71 int dinic(int ban){
 72     int ret=0;
 73     while(bfs(ban)){
 74         memcpy(cur,head,sizeof(cur));
 75         ret+=dfs(st,0x3f3f3f3f);
 76     }
 77     return ret;
 78 }
 79 int main(){
 80     init();ed=200000;
 81     scanf("%d",&n);
 82     while(true){
 83         ans++;
 84         addedge(st,ans*2-1,1);
 85         addedge(ans*2,ed,1);
 86         for(int i=1;i<ans;i++){
 87             int a=sqrt(ans+i);
 88               if(a*a!=ans+i)continue;
 89             addedge(i*2-1,ans*2,1);
 90         }
 91         num+=1-dinic(-1);
 92         if(num>n){
 93             printf("%d\n",ans-1);
 94             dinic(ans*2);
 95             for(int i=1;i<ans;i++){
 96                 usd[(pre[2*i]+1)/2]=true;
 97             }
 98             for(int i=1;i<ans;i++){
 99                 if(!usd[i]){
100                     int now=i;tp=0;
101                     while(now){
102                         pr[++tp]=now;
103                         now=(pre[now*2]+1)/2;
104                     }
105                     for(int i=tp;i>=1;i--){
106                         printf("%d",pr[i]);
107                         if(i!=1)printf(" ");
108                     }
109                     printf("\n");
110                 }
111             }
112             break;
113         }
114     }
115     return 0;
116 }

 

posted @ 2018-12-15 21:35  Mr_Handsome  阅读(153)  评论(0编辑  收藏  举报