浙江大学考研机试题——负载均衡

题目描述

N N N 个石子堆成一堆。

你可以对石子堆进行拆分操作,具体为选中一堆现有石子堆,将其中的石子分为两堆(不能为空)。

此外,在任何时候,最小石子堆的石子数量都必须严格大于最大石子堆的石子数量的一半。

我们希望,通过将石子堆进行拆分,最终可以满足:

石子堆的总数量尽可能大,不妨设这个总数量的最大可能值为 M M M
满足上一条要求的前提下,最大石子堆石子数量与最小石子堆石子数量之差尽可能小,不妨设这个差的最小可能值为 D D D
请你计算并输出 M M M D D D 的值。

输入格式

一个正整数 N N N

输出格式

一行,输出 M M M D D D

数据范围

2 ≤ N ≤ 200 2≤N≤200 2N200

输入样例:

22

输出样例:

4 1

样例解释

N N N= 22 22 22 时,最多可以拆分为 4 4 4 个石子堆。

以下是可行方案。

方案一:

22
= 8 + 14
= 8 + 7 + 7
= 4 + 4 + 7 + 7
方案二:

22
= 10 + 12
= 10 + 6 + 6
= 4 + 6 + 6 + 6
方案三:

22
= 10 + 12
= 10 + 6 + 6
= 5 + 5 + 6 + 6
可以看出,采用最后一种方案,最大石子堆石子数量与最小石子堆石子数量之差最小,为 6−5=1。

思路:

我们分析会发现我们只能找最多的一堆来拆。
现在我们我们有两堆,a和b,且b<=a,我们拆b。现在我们不知道具体数的范围且我们要满足最小石子堆的石子数量都必须严格大于最大石子堆的石子数量的一半。b拆分的最小值应该是b/2。b/2一定<=a/2的。一定不满足条件。所以我们发现每次我们只能拆最大的一堆。如果所有数都小于等于最大的堆我们就应该停止划分。
现在我们来分类讨论,只有一堆和大于等于二堆的情况
1:如果只有一堆:我们要拆成两堆,设两堆数为x,y且x<=y。x+y=a。拆完后 x>y/2 等价于2x>y.带入x+y=a。我们会得到x>a/3。又由于 x<=y , y=a-x 所以 x<=a/2 。所有x的范围是(a/3,a/2]。
2:如果至少有两堆。我们要先找到一个最大值和一个次大值:a和b 。将a拆分为x+y=a,且x要大于b/2。所以我们的范围是 (max(a/3,b/2),a/2]。

代码如下:

#include<iostream>
#include<algorithm>
using namespace std;

const int N=210;

int n;
int w[N],cnt;// w[]存储每一堆石子数量 cnt是石子堆的个数
int m,d;// 答案M 和 D

int getd()//求最大石子堆石子数量与最小石子堆石子数量之差
{
    int x=0,y=N;
    for(int i=0;i<cnt;i++)
    {
        x=max(x,w[i]);
        y=min(y,w[i]);
    }
    return x-y;
}

void dfs()
{
    if(cnt>m) m=cnt,d=getd();//现在的石子堆数>上一次的,更新M和D
    else if(cnt==m) d=min(d,getd());//相等只更新 D
    
    if(cnt==1)//只有一堆的情况
    {
        int a=w[0];
        for(int x=a/3+1;x<=a/2;x++)
        {
            w[0]=x;
            w[cnt++]=a-x;
            dfs();
            //恢复现场
            w[0]=a;
            cnt--;
        }
    }
    else
    {
        int i=0,j=-1;
        for(int k=1;k<cnt;k++)//找到第一,第二大的值的下标
            if(w[k]>=w[i]) j=i,i=k;
            else if(j==-1||w[k]>w[j]) j=k;
            
        int a=w[i],b=w[j];
        if(a>b)
        {
            for(int x=max(b/2,a/3)+1;x<=a/2;x++)
            {
                w[i]=x,w[cnt++]=a-x;
                dfs();
                //恢复现场
                w[i]=a,cnt--;
            }
        }
        else return;
    }
}

int main()
{
    cin>>n;
    w[cnt++]=n;//初始化只有一堆,w[0]=n,cnt=1
    
    dfs();//暴力枚举所有方案数
    
    cout<<m<<" "<<d<<endl;
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值