网络流二十四题之四 —— 魔术球问题(BALL)

24 篇文章 0 订阅
5 篇文章 0 订阅

最小路径覆盖问题


Description

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


Input

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


Output

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


Sample Input

4


Sample Output

11
1 8
2 7 9
3 6 10
4 5 11


Solution


方法一(网络流):

每根柱子互不干扰,可以把每根柱子看成一条路径——这不就成了最小路径覆盖问题了吗???

将相加为完全平方数的两个数连一条边,求解最小路径覆盖问题就可以解决 n0 个球最少需要多少根柱子的问题。

然后依次枚举 n0 ,直到所需柱子的个数大于 n ,即可得出答案(这里用枚举的原因是,每次不需要重新求解最大流)。


方法二(贪心):

还有一种贪心的方法。
现在如果有 T 个柱子,要放数 i ,从第一个柱子开始试,试到能放的那个柱子,就把 i 放进去,如果 T 个柱子都不行,就再多增加一个柱子放 i

易知,这样得出的答案为 f(n)={(n21)/2+n   n1(mod 2)(n22)/2+n   n0(mod 2)

下面,证明一下这个贪心的正确性。

n0(mod 2) ,假设 ans(n)>f(n) ,则 ans(n) 最小为 f(n)+1

此时,这 ans(n) 个数为 123(n21)/2+n+1

易知在前 n+1 个数中,最大的两个数之和为
(n21)/2+n+1+(n21)/2+n=n2+2n<(n+1)2

最小的两个数之和为
[(n21)/2+n+1(n+1)+1]+[(n21)/2+n+1(n+1)+2]=n2+2>n2

即这 n+1 个数任意两个数之和都夹在两个完全平方数之间,即任意两个数之和都不为完全平方数。
所以,这 n+1 个数都不能放在同一根柱子上,与只有 n 根柱子相矛盾。

同理,当 n1(mod 2) 时,也可以用反证法证明这个贪心的正确性。

综上所述,这个贪心是正确的。


Code(只给出网络流版本)

[cpp]
  1. #include <iostream>  
  2. #include <cstdio>  
  3. #include <cstring>  
  4. #include <cmath>  
  5. #include <queue>  
  6.   
  7. #define Min(x,y) ((x)<(y)?(x):(y))  
  8. #define Max(x,y) ((x)>(y)?(x):(y))  
  9.   
  10. using namespace std;  
  11.   
  12. const int INF=0x3f3f3f3f;  
  13.   
  14. int m,n,s,t=100000-1,cnt,nx;  
  15. int weight;  
  16.   
  17. int low[1000100],head[1000100],nxt[1000100],data[1000100];  
  18. int dis[1000100];  
  19. bool vis[1000100];  
  20. bool he[1000010];  
  21. queue<int>q;  
  22.   
  23. void add(int x,int y,int z){  
  24.     nxt[cnt]=head[x];data[cnt]=y;low[cnt]=z;head[x]=cnt++;  
  25.     nxt[cnt]=head[y];data[cnt]=x;low[cnt]=0;head[y]=cnt++;   
  26. }  
  27.   
  28. bool BFS(){  
  29.     memset(dis,-1,sizeof dis);  
  30.     q.push(s);dis[s]=0;  
  31.     while(!q.empty()){  
  32.         int now=q.front();q.pop();  
  33.         for(int i=head[now];i!=-1;i=nxt[i])  
  34.             if(low[i]&&dis[data[i]]<0){dis[data[i]]=dis[now]+1;q.push(data[i]);}  
  35.     }  
  36.     return dis[t]>0;  
  37. }  
  38.   
  39. int dfs(int now,int flow){  
  40.     if(now==t)return flow;  
  41.     int Flow;  
  42.     for(int i=head[now];i!=-1;i=nxt[i]){  
  43.         if(low[i]&&dis[data[i]]==dis[now]+1){  
  44.             if(Flow=dfs(data[i],Min(flow,low[i]))){  
  45.                 low[i]-=Flow;  
  46.                 low[i^1]+=Flow;  
  47.                 return Flow;  
  48.             }  
  49.         }  
  50.     }  
  51.     return 0;  
  52. }  
  53.   
  54. void dfs2(int now){  
  55.     printf(”%d ”,now);  
  56.     vis[now]=true;  
  57.     for(int i=head[now];i!=-1;i=nxt[i]){  
  58.         if(!vis[data[i]]&&data[i]!=s&&data[i]!=t&&data[i]>n&&!low[i])dfs2(data[i]-2000);  
  59.     }  
  60. }  
  61.   
  62. int work(int front){  
  63.     weight++;  
  64.     n=front;  
  65. //  for(int i=0;i<cnt;i+=2){  
  66. //      low[i]=1;  
  67. //      low[i+1]=0;  
  68. //  }  
  69.     for(int i=1;i<front;i++){  
  70.         if(he[i+front])add(i,front+2000,1);  
  71.     }  
  72.     add(s,front,1);add(front+2000,t,1);  
  73.     while(BFS()){  
  74.         int flag;  
  75.         while(flag=dfs(s,INF)){  
  76.             weight-=flag;  
  77.         }  
  78.     }  
  79.     return weight;  
  80. }  
  81.   
  82. void work2(int front){  
  83.     memset(head,-1,sizeof head);  
  84.     cnt=0;  
  85.     n=front;  
  86.     for(int i=1;i<=front;i++)  
  87.         for(int j=i+1;j<=front;j++)  
  88.             if(he[i+j])add(i,j+2000,1);  
  89.     for(int i=1;i<=front;i++){add(s,i,1);add(i+2000,t,1);}  
  90.     while(BFS()){  
  91.         int flag;  
  92.         while(flag=dfs(s,INF));  
  93.     }  
  94. }  
  95.   
  96. int main(){  
  97.     freopen(”ball.in”,“r”,stdin);  
  98.     freopen(”ball.out”,“w”,stdout);   
  99.     memset(head,-1,sizeof head);  
  100.     int ans=0,ll;  
  101.     scanf(”%d”,&nx);  
  102.     for(int i=1;i<=100;i++)  
  103.         he[i*i]=true;  
  104.     for(ll=1;;ll++){  
  105.         int tmp=work(ll);  
  106.         bool flag=(tmp<=nx);  
  107.         if(!flag)break;  
  108.     }  
  109.     printf(”%d\n”,ll-1);  
  110.     work2(ll-1);  
  111.     for(int i=1;i<ll;i++)if(!vis[i]){  
  112.         dfs2(i);  
  113.         printf(”\n”);  
  114.     }  
  115.     return 0;  
  116. }  
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值