“玲珑杯”ACM比赛 Round #23 C -- 你居然不吃巧克力(思路)

2017年就酱结束了,逐渐步入中年生活(ಥ_ಥ)  ,希望2018年将自己技能树点的丰富一点,还有早日脱单?

还是和纸片人谈恋爱实在( ̄▽ ̄")

题目:

DESCRIPTION

给定一个正整数n,现在有n个石头,每个单独成一堆。
现在可以每次合并两堆石头,产生的能量为两堆石头个数的min。
你现在要将所有的石头合并成一堆,并且获得的能量最大。
输出这个最大值。
1 ≤ n ≤ 1e7。

INPUT
第一行是一个正整数T (1 ≤ T ≤ 10)表示数据组数,接下来T行每行一个正整数。数据满足一个测试点中,最多只有1个n超过1e6。
OUTPUT
T行,每行一个正整数。
SAMPLE INPUT
3135
SAMPLE OUTPUT
025

思路:

最优策略为差值最小的数两两相加,理由是……用心感受? (ಥ_ಥ)    
以13为例最优解为
                 8+5
      4+4                 4+1
2+2      2+2     2+2      1
……
不难发现欲使差值最小的数相加,会变成该数 二进制最高位 + 二进制其他位 之和
那么可以将该数转换为二进制数(1101),计算每一位1的贡献值,
如首位1即8
     8=4+4=2+2+2+2=1+1+1+1+1+1+1+1
贡献值=4+2*2+1*4+较低位的和(即5)

可得每一位1的贡献值为
cnt*f(i)+i*f(i-1) //cnt为该数位数的较高位1出现次数,i为该数2的i次,f(i)为2的i次

核心代码就酱出现啦

//index为二进制串长度
int ans=0;
for(i=0;i<index;i++){
    if(v[i]==1){
        cnt--;
        ans+=cnt*f(i)+i*f(i-1);
    }
}

代码:

#include <stdio.h>
#include<string.h>
int v[100];
//返回2的n次
int f(int n){
    int i,sum=1;
    for(i=0;i<n;i++)
        sum=2*sum;
    return sum;
}
int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        memset(v,0,sizeof(v));
        int n,index=0,i,cnt=0,ans=0;
        scanf("%d",&n);
        
//二进制存储,cnt表示有几个1
        while(n){
            if(n%2){
                v[index]++;
                cnt++;
            }
            index++;
            n/=2;
        }
        for(i=0;i<index;i++){
            if(v[i]==1){
                cnt--;
                ans+=cnt*f(i)+i*f(i-1);
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值