ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Codeforces Round #765 (Div. 2) A~C 题解(原来如此简单,超级详细入门必备)

2022-01-13 16:58:32  阅读:174  来源: 互联网

标签:int 题解 Codeforces 765 二进制 read 数组 ans da


A. Ancient Civilization

题目传送门


题意:给你一个数组a(十进制),长度为n,0\leq a[i]\leq 2^{l}-1,现在要求你拿出一个ans(十进制),0\leq ans\leq 2^{l}-1,使得这a[i](二进制)与ans(二进制)对应位置差异度和最小。

举个栗子:a[i]=5的二进制是101,ans=10的二进制是1010,咱们从右往左看,发现第一位,第二位,第三位,第四位都不一样,现在的差异度就是4,而差异度和就是\sum_{i=1}^{n}d(a[i],ans)

思路:这题的难点就是看懂题(嘿嘿,看懂题应该大家就会了吧),那其实是这就是一个选择问题,ans(二进制)位数最多就是 l 位,我们只需要考虑当前第 i 位取与不取,为了让差异度最小,那么我们pre数组存所有数(二进制)第 i 位的 1 的个数,然后,若当前第 i 位的 1 的个数大于了\frac{1}{2}n就取(一半的时候随便取不取,我这里没有取)

核心代码:

if(pre[i]>n-pre[i])b[i]=1;//b[]数组用来存ans的二进制

快速幂:

int kuai(int s,int n){
	int ans=1;
	while(n){
		if(n&1)ans*=s;
		s*=s;
		n>>=1;
	}
	return ans;
}

快读:

inline int read() {
    char ch = getchar(); int x = 0, f = 1;
    while(ch < '0' || ch > '9') {
        if(ch == '-') f = -1;
        ch = getchar();
    } while('0' <= ch && ch <= '9') {
        x = x * 10 + ch - '0';
        ch = getchar();
    } return x * f;
}

而要实现这一切我们还得有一个十进制转换二进制(都2022年了博主还是只能手搓),和最后快速幂(当然可以用pow,但是pow是double类型的,出了问题你想不到)求ans答案,那么话说了这么多,上代码

t=read();//t组数据,这里博主用了快读,减少时间
while(t--){
	n=read();l=read();//n个数,l是二进制最大位数
	for(i=1;i<=n;i++)a[i]=read();
	for(i=1;i<=n;i++){
		da1=a[i];sum=1;        //
		while(da1){            //手搓十进制转二进制
			if(da1%2){         //
				pre[sum]++;    //
			}                  //
			da1>>=1;           //
			sum++;             //
		}
	}
	for(i=1;i<=l;i++)if(pre[i]>n-pre[i])b[i]=1;//计算ans(二进制)
	ans=0;//可别忘记更新ans哦,t组数据
	for(i=1;i<=l;i++)if(b[i])ans+=kuai(2,i-1);//手搓二进制转十进制,kuai(2,i-1)是快速幂这里博主就不放出来了
	cout<<ans<<endl;
	for(i=1;i<=l;i++)pre[i]=0;//别忘记了跟新pre数组状态
}
return 0;

B. Elementary Particles

题目传送门


题意:t组数据,给你一个长度为n的数组a,现在要求你在数组中截取两个不相同但是长度相等的子序列,唯一要求是使得至少存在一个对应的位置上面数字一样,求最长长度是多少(不存在则输出-1)

 

比如1 2 3和5 2 7就是满足条件的,因为他们第二个位置相等,而1 3 2和2 1 3就不满足条件

思路:既然这是一道存在性的题目,那么偷奸耍滑的我们肯定直接考虑只要有一个相等的存在即可,现在我们给你一个a数组a_{1},a_{2},a_{3},a_{4},a_{5},a_{6},a_{7} ,a_{8}此时若a_{2}a_{6}相等,那么此时由他们构成的数列最大是多少呢?是a_{1},a_{2},a_{3},a_{4}a_{5},a_{6},a_{7},a_{8}。我们发现只要a_{2}的左边序列的长度,和a_{6}右边序列的长度,为什么这么说呢?因为a_{2}a_{6}的左边,因此a_{2}左边能取到多长,a_{6}一定能取到,所以我们看a_{2}的左边,同理,这也是我们只看a_{6}右边的理由

关键步骤:因此我们只需要利用sort进行结构体排序(利用结构题,在存下每个数的时候,将它所在的位置也存下来)

关键公式:我们现在令a_{2}的位置为 l ,令a_{6}的位置为 r ,则长度sum=l-1+n-r+1=l+n-r,因为n为已知量,所以我们只需要考虑 l - r 最小,又因为我们结构体排序过了,所以我们只要需要算相邻两个值的差绝对值最小的那一个(前提是这两个数的值相等),最后和n相加即可

关键代码:

cmp:

bool cmp(node x,node y)
{
	if(x.da==y.da)return x.xu<y.xu;//其实这步有些多余,不过为了安心还是写上去了,因为如何一样cmp是不会继续排下去的 
	return x.da<y.da;
}

 对啦,写的时候就利用尺取的思想,l,r一起动就好啦

上代码:

struct node{
	int xu,da;//xu是位置,da则是值 
}a[500005];
bool cmp(node x,node y)
{
	if(x.da==y.da)return x.xu<y.xu;//其实这步有些多余,不过为了安心还是写上去了,因为如何一样cmp是不会继续排下去的 
	return x.da<y.da;
}
signed main()
{
	int i,j,t;int n,l,r,sum;//n个数,t组数据,l,r当前排过序后数列的位置,sum则是值 
//	jian1;jian2;jian3;//l,r位置的解读,例如现在1,8,3,现在l为2则在当前数列的位置是2
	t=read();//快读 
	while(t--){
		n=read();
		for(i=1;i<=n;i++){
			a[i].da=read();
			a[i].xu=i;//存当前数的位置 
		}
		sort(a+1,a+1+n,cmp);//排序 
		sum=-1;l=1;r=2;//l一定是小于r,当然你想要让他等于改一下后面就好了
		while(r<=n){//r可不能出界哦(⊙o⊙),一共就n个数诶 
			if(a[l].da==a[r].da)sum=max(sum,a[l].xu+n-a[r].xu);//毕竟最后的答案可是求最值,所以得比较比较 
			l++;r++;//l当前位置的数被用过了 ,已经没了价值所以l++ 
		}
		cout<<sum<<endl;//输出答案 
	}
	return 0;
}

C. Road Optimization

题目传送门

题意:一条路长度为len,n给限速标(第一个限速标一定再起点上面),用长度为n数组a表示的坐标(单位是公里),再给一个与数组a对应的b数组,记录第 i 个标到第 i+1 个标类每公里所需要的时间,算法为:(a[i+1]-a[i])*b[i],现在允许你最多删除k个点(也可以不删(⊙.⊙)),求\sum_{i=1}^{n}(a[i+1]-a[i])*b[i]最小

思路:此题看一眼,嗯...发现没有思路,看第二眼,嗯...没有思路,摆了摆了

其实此题应该用动态规划,局部最优使全局最优(前提是没有后效性,即前面你无论怎么选都不会影响到后面的选择)因此我写出dp_{i,p}意思是指保留第 i 个限速标(即不删除),然后现在还有p个删除的名额(即还可以删除p个限速标),现在关键是dp方程如何写

关键代码:

dp方程:dp_{i,p}=min(dp_{i,p},dp_{j,p}+(a[i]-a[j])*b[j])

这里我就先把dp方程给了出来,里面的 j 是什么呢,里面的 j 是从第 j 个到第 i 个之内的限速标q全部删除(不包括 i,j ),对我们令最后的终点也是一个点,即a[n+1]=len

这里你可能会问,因为是连着删除不会漏解吗?当然不会,现在解惑:

首先我们的第 i 个保留,这就使得我们解决了后效性的问题,因为无论前面怎么删除,就算捅破了天,都不会影响后面的删除,其次,j 是小于 i 的,是指从第 j 个到第 i 个之内的限速标q全部删除(不包括 i,j ),所以我们是步步最优

废话不多说,直接上代码:

signed main()
{
	int i,j,t;int n,len,p,k;
	n=read();
	len=read();
	k=read();
	for(i=1;i<=n;i++)a[i]=read();
	for(i=1;i<=n;i++)b[i]=read();
	a[++n]=len;
	for(i=1;i<=n;i++)for(j=0;j<=k;j++)dp[i][j]=1e15;//我们得先将dp数组初始话,毕竟我们是一步一步去比较最小值,所以我们初始化为最大值 
	dp[1][k]=0;
	for(i=2;i<=n;i++)
		for(j=1;j<i;j++)
			for(p=0;p<=k;p++)//要使用dp方程首先要保证咱们现在删除的路标没有超出k个,i和j之间有i-j-1个 
				if(p+i-j-1<=k)dp[i][p]=min(dp[i][p],dp[j][p+i-j-1]+(a[i]-a[j])*b[j]);
	cout<<*min_element(&dp[n][0],&dp[n][k+1])<<endl;//因为答案是一步一步更新上来的,所以我们只需要找到在n处,删除0-k个的最小值 
	return 0;
}

由于博主太菜,当时没有写出来,赛后补题++

希望大家看了这篇文章都有进步,加油!(⊙0⊙)!

对了推一个知乎上面大佬的题解Codeforces Round #765 (Div. 2) A~D - 知乎 (zhihu.com)

博主的dp就是和这个大佬学的,主要是大佬讲的有点深奥,博主理解了好久

标签:int,题解,Codeforces,765,二进制,read,数组,ans,da
来源: https://blog.csdn.net/qq_62309808/article/details/122472069

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有