CF1188D Make Equal

一、题目

点此看题

二、解法

首先把要求的东西翻译一下,其实就是找到一个 (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]);
}
原文地址:https://www.cnblogs.com/C202044zxy/p/15062207.html