2017年就酱结束了,逐渐步入中年生活(ಥ_ಥ) ,希望2018年将自己技能树点的丰富一点,还有早日脱单?
还是和纸片人谈恋爱实在( ̄▽ ̄")
题目:
给定一个正整数n,现在有n个石头,每个单独成一堆。
现在可以每次合并两堆石头,产生的能量为两堆石头个数的min。
你现在要将所有的石头合并成一堆,并且获得的能量最大。
输出这个最大值。
1 ≤ n ≤ 1e7。
思路:
最优策略为差值最小的数两两相加,理由是……用心感受? (ಥ_ಥ)
以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;
}