一、题目
二、解法
首先把要求的东西翻译一下,其实就是找到一个 (xgeq 0),使得下列式子最小:
[sum cnt(x+max a-a_i)
]
其中 (cnt(x)) 表示 (x) 二进制位中 (1) 的个数,为了方便我们使 (a_ileftarrowmax a-a_i)
如果 (dp) 求这个式子的最小值那么肯定是按位规划,我们考虑以下信息:
- (x) 这一位规划 (0/1) 的代价(转移方法)
- 所有 (a_i) 在这一位出现情况(已知条件)
- (a_i) 是否在之前的规划中出现过进位(状态)
现在问题就是进位的状态数可能达到 (O(2^n)),但是考虑如果只保留前 (i-1) 位,越大的 (a_i) 越有可能进位。我们把所有 (a_i) 按此从小到大排序,那么发现进位的一定是 (a) 的一个前缀。
设 (dp[i][j]) 为考虑到第 (i) 位,有 (j) 个数发生进位的最小代价,时间复杂度 (O(nlog nlog a))
三、总结
要想着怎么套用动态规划模型,比如此题你就不能按数字规划(请问你这样规划了个啥)
考虑有效状态是 (dp) 优化的常见方式。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int M = 100005;
#define int long long
int read()
{
int x=0,f=1;char c;
while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
int n,k,mx,ans,a[M],s[2][M],f[70][M];
int cmp(int x,int y)
{
return (x&((1ll<<k)-1))<(y&((1ll<<k)-1));
}
signed main()
{
n=read();
for(int i=1;i<=n;i++)
{
a[i]=read();
mx=max(mx,a[i]);
}
for(int i=1;i<=n;i++)
a[i]=mx-a[i];
memset(f,0x3f,sizeof f);
f[0][0]=0;ans=1e18;
for(k=0;k<60;k++)
{
sort(a+1,a+1+n,cmp);
for(int i=1;i<=n;i++)
{
s[0][i]=s[0][i-1];s[1][i]=s[1][i-1];
s[(a[i]>>k)&1][i]++;
}
for(int i=0;i<=n;i++)
{
int w,p;
w=s[1][n-i]+s[0][n]-s[0][n-i],p=s[1][n]-s[1][n-i];
f[k+1][p]=min(f[k+1][p],f[k][i]+w);
w=s[0][n-i]+s[1][n]-s[1][n-i];p=n-s[0][n-i];
f[k+1][p]=min(f[k+1][p],f[k][i]+w);
}
}
printf("%lld
",f[60][0]);
}