Educational Codeforces Round 76 (Rated for Div. 2) 补题(E dp、F 折半枚举+hash)

思路来源

https://www.cnblogs.com/mooleetzi/p/11859365.html

E The Contest(dp)

第一个人有k1张牌,第二个人有k2张牌,第三个人有k3张牌(1<=k1,k2,k3<=2e5,k1+k2+k3<=2e5)

n=k1+k2+k3,n张牌构成1到n的一个排列,问最少交换多少次,

使得第一个人手里的牌对应一个1到i的前缀,

第三个人手里的牌对应一个j到n的后缀,

第二个人的牌为[i+1,j],允许所有牌只掌握在一个人或两个人手中

题解

pos[i]:记录下i这个值在哪个人手中

增序访问1到n,相当于把n个数分成三个区间,类似区间dp

区间dp是判断当前这个数是否新开一段,这里是判断当前这个数在第几段

dp[N][3],代表第i个数放在第j个人手中

代码

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
const int INF=0x3f3f3f3f;
//dp[i][j]:��i��ֵ ���ڵ�j������ 
//���� ö�ٷֽ�������dp �ֳ����� 
int dp[N][3],pos[N],v;
int n,k[3];
int main()
{
	memset(dp,INF,sizeof dp);
	for(int i=0;i<3;++i)
	{
		scanf("%d",&k[i]);
		n+=k[i];
	}
	for(int i=0;i<3;++i)
	{
		for(int j=0;j<k[i];++j)
		{
			scanf("%d",&v);
			pos[v]=i;
		}
		dp[0][i]=0;
	}
	for(int i=1;i<=n;++i)
	{
		for(int j=0;j<3;++j)//��� 
		{
			for(int l=0;l<=j;++l)//ǰ�� 
			{
				dp[i][j]=min(dp[i][j],dp[i-1][l]+(pos[i]!=j));
			}
		}
	}
	int ans=INF;
	for(int i=0;i<3;++i) 
	ans=min(ans,dp[n][i]);
	printf("%d\n",ans);
	return 0;
}

F Make Them Similar(折半枚举+hash)

给你n(2<=n<=100)个数,第i个数ai(ai<2^30),

判断是否存在一个自然数x,令bi=ai异或x,

每个bi在二进制表示下中1的个数都相同

能的话输出x,否则输出-1

题解

①最暴力的做法,

考虑最终结果,bi在二进制表示下中1的个数为sum,

sum从0到30,枚举sum,对于每个sum,折半枚举前15位和后15位

相当于判断前15位的数组c[]={1,2,3,4,5}和后15位数组d[]={5,4,3,2,1}是否和相等

即判断对于当前数组{a,b,c,d},是否存在{sum-a,sum-b,sum-c,sum-d}

对于数组的hash,可以用map对vector哈希,

由于c[]和d[]中元素只会在0到15之间,所以sum-a只会在0到30之间,

可以用31和2^64次方(自然溢出)双哈希

判断a[0]+b[0]==a[1]+b[1]+...=a[n]+b[n]=sum

即a[0]-a[1]=b[1]-b[0],a[0]-a[2]=b[2]-b[0]

因此,构造两个序列,一个是a[0]减a[i]的数组,一个是b[i]减b[0]的数组

无需知道sum,只需判断这两个数组相同即可,故不用枚举sum

减的值的范围[-15,15],加偏移量15之后[0,30]

此外,前一发代码写的常数比较大,可以用数组预处理出1<<15内的值二进制位中1的个数,

用空间换时间,干掉pop_count的一个log,

但其实此题复杂度较为玄学,或许是数据较弱

代码1(暴力)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N=105;
int low,high,ans,sum;
int n,a[N],b[N],c[N],now;
unordered_map<ull,int>q;
void hs(int d[],int len,int x)
{
	ull now=0;
	for(int i=0;i<len;++i)
	now=now*31+d[i];
	q[now]=x;
}
int ok(int d[],int len,int x)
{
	ull now=0;
	int v;
	for(int i=0;i<len;++i)
	{
		v=sum-d[i];
		if(v<0)return -1;
		now=now*31+v;
	}
	if(q.count(now))return x|q[now];
	return -1;
} 
int main()
{
	scanf("%d",&n);
	for(int i=0;i<n;++i)
	scanf("%d",&a[i]);
	int up=1<<15; 
	for(int i=0;i<up;++i)
	{
		for(int j=0;j<n;++j)
		{
			low=a[j]&(up-1);
			c[j]=__builtin_popcount(low^i);
		}
		hs(c,n,i);
	}
	ans=-1;
	for(sum=0;sum<=30;++sum)
	{
		for(int i=0;i<up;++i)
		{
			for(int j=0;j<n;++j)
			{
				high=a[j]&(~(up-1));
				c[j]=__builtin_popcount(high^(i<<15));
			}
			ans=ok(c,n,i<<15);
			if(~ans)break;
		}
		if(~ans)break;
	}
	printf("%d\n",ans);
	return 0;
} 

代码2(优雅的暴力)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N=105;
int low,high,ans,sum;
int cnt[1<<15];
int n,a[N],b[N],c[N],now;
unordered_map<ull,int>q;
void hs(int d[],int len,int x)
{
	ull now=0;
	for(int i=0;i<len;++i)
	now=now*31+(d[i]+15);
	q[now]=x;
}
int ok(int d[],int len,int x)
{
	ull now=0;
	for(int i=0;i<len;++i)
	now=now*31+(d[i]+15);
	if(q.count(now))return x|q[now];
	return -1;
} 
int main()
{
	scanf("%d",&n);
	for(int i=0;i<n;++i)
	scanf("%d",&a[i]);
	int up=1<<15; 
	for(int i=0;i<up;++i)
	cnt[i]=cnt[i&(i-1)]+1;
	for(int i=0;i<up;++i)
	{
		for(int j=0;j<n;++j)
		{
			low=a[j]&(up-1);
			c[j]=cnt[low^i];
		}
		for(int j=n-1;j>=0;--j)
		{
			c[j]=c[0]-c[j];
		}
		hs(c,n,i);
	}
	ans=-1;
	for(int i=0;i<up;++i)
	{
		for(int j=0;j<n;++j)
		{
			high=a[j]&(~(up-1));
			c[j]=cnt[(high^(i<<15))>>15];
		}
		for(int j=n-1;j>=0;--j)
		{
			c[j]=c[j]-c[0];
		}
		ans=ok(c,n,i<<15);
		if(~ans)break;
	}
	printf("%d\n",ans);
	return 0;
} 
发布了467 篇原创文章 · 获赞 53 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/Code92007/article/details/103074307