您的位置:首页 > 理论基础 > 计算机网络

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

2016-05-07 11:20 295 查看

最小路径覆盖问题

Description

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

(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)={(n2−1)/2+n n≡1(mod 2)(n2−2)/2+n n≡0(mod 2)。

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

若 n≡0(mod 2),假设 ans(n)>f(n),则 ans(n) 最小为 f(n)+1。

此时,这 ans(n) 个数为 1、2、3、…(n2−1)/2+n+1。

易知在前 n+1 个数中,最大的两个数之和为

(n2−1)/2+n+1+(n2−1)/2+n=n2+2n<(n+1)2

最小的两个数之和为

[(n2−1)/2+n+1−(n+1)+1]+[(n2−1)/2+n+1−(n+1)+2]=n2+2>n2

即这 n+1 个数任意两个数之和都夹在两个完全平方数之间,即任意两个数之和都不为完全平方数。

所以,这 n+1 个数都不能放在同一根柱子上,与只有 n 根柱子相矛盾。

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

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

Code(只给出网络流版本)

[cpp] #include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <queue>

#define Min(x,y) ((x)<(y)?(x):(y))
#define Max(x,y) ((x)>(y)?(x):(y))

using namespace std;

const int INF=0x3f3f3f3f;

int m,n,s,t=100000-1,cnt,nx;
int weight;

int low[1000100],head[1000100],nxt[1000100],data[1000100];
int dis[1000100];
bool vis[1000100];
bool he[1000010];
queue<int>q;

void add(int x,int y,int z){
nxt[cnt]=head[x];data[cnt]=y;low[cnt]=z;head[x]=cnt++;
nxt[cnt]=head[y];data[cnt]=x;low[cnt]=0;head[y]=cnt++;
}

bool BFS(){
memset(dis,-1,sizeof dis);
q.push(s);dis[s]=0;
while(!q.empty()){
int now=q.front();q.pop();
for(int i=head[now];i!=-1;i=nxt[i])
if(low[i]&&dis[data[i]]<0){dis[data[i]]=dis[now]+1;q.push(data[i]);}
}
return dis[t]>0;
}

int dfs(int now,int flow){
if(now==t)return flow;
int Flow;
for(int i=head[now];i!=-1;i=nxt[i]){
if(low[i]&&dis[data[i]]==dis[now]+1){
if(Flow=dfs(data[i],Min(flow,low[i]))){
low[i]-=Flow;
low[i^1]+=Flow;
return Flow;
}
}
}
return 0;
}

void dfs2(int now){
printf(”%d ”,now);
vis[now]=true;
for(int i=head[now];i!=-1;i=nxt[i]){
if(!vis[data[i]]&&data[i]!=s&&data[i]!=t&&data[i]>n&&!low[i])dfs2(data[i]-2000);
}
}

int work(int front){
weight++;
n=front;
// for(int i=0;i<cnt;i+=2){
// low[i]=1;
// low[i+1]=0;
// }
for(int i=1;i<front;i++){
if(he[i+front])add(i,front+2000,1);
}
add(s,front,1);add(front+2000,t,1);
while(BFS()){
int flag;
while(flag=dfs(s,INF)){
weight-=flag;
}
}
return weight;
}

void work2(int front){
memset(head,-1,sizeof head);
cnt=0;
n=front;
for(int i=1;i<=front;i++)
for(int j=i+1;j<=front;j++)
if(he[i+j])add(i,j+2000,1);
for(int i=1;i<=front;i++){add(s,i,1);add(i+2000,t,1);}
while(BFS()){
int flag;
while(flag=dfs(s,INF));
}
}

int main(){
freopen(”ball.in”,“r”,stdin);
freopen(”ball.out”,“w”,stdout);
memset(head,-1,sizeof head);
int ans=0,ll;
scanf(”%d”,&nx);
for(int i=1;i<=100;i++)
he[i*i]=true;
for(ll=1;;ll++){
int tmp=work(ll);
bool flag=(tmp<=nx);
if(!flag)break;
}
printf(”%d\n”,ll-1);
work2(ll-1);
for(int i=1;i<ll;i++)if(!vis[i]){
dfs2(i);
printf(”\n”);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: