Codeforces Round 676 完整题解

本文精选了五道算法竞赛题目并提供了详细的解答思路,包括异或运算性质的应用、地图路径规划、字符串操作构造、蜂窝图最短路径及数列操作优化等问题。

A.XORwice
题意:
给定a,b,你可以随机选择一个数x,求(a⊕x)+(b⊕x)的最小值。
题解:
在二进制上每一位考虑,如果某一位都是1或0,那么x的这一位就是0,如果一个是1,一个是0,那么他x不管怎么选这一位都会留下。这个过程跟异或是一样的,所以就是a^b。

int main() {
    for(scanf("%d",&_);_;_--){
        a=rd();b=rd();
        printf("%lld\n",a^b);
    }
    return 0;
}

B.Putting Bricks in the Wall
题意:
给定一个由01组成的地图,一条路径必须经过相同的点(全为0或全为1)。起点(1,1)和终点(n,n)不受上面的规则限制,问你在最多修改两个格子的情况下,如何使得起点到终点没有路径。
题解:
稍微思考一下就能明白,地图中很多点修改它没有意义,因为它总可能想一种办法让你的修改失效。比较有特征的就是起点旁边和终点旁边的点,这样的点只有4个。我们保证(2,1)(1,2)相同,(n,n-1)(n-1,n)相同且与(1,2)不相同即可

int _;
int n;
char s[maxn][maxn];
struct node{
	int x,y;
};
int main() {
    for(scanf("%d",&_);_;_--){
        scanf("%d%*c",&n);
        for (int i=1;i<=n;i++)scanf("%s",s[i]+1);
        int ans=0;
    	vector<node>v;
    	if (s[2][1]==s[1][2]){
    		if (s[n-1][n]==s[2][1]){ans++;v.push_back({n-1,n});}
    		if (s[n][n-1]==s[2][1]){ans++;v.push_back({n,n-1});}
    	}
    	else{
    		if (s[n-1][n]==s[n][n-1]){
    			if (s[2][1]==s[n][n-1]){ans++;v.push_back({2,1});}
    			if (s[1][2]==s[n][n-1]){ans++;v.push_back({1,2});}
    		}
    		else{
    			if (s[1][2]=='1'){ans++;v.push_back({1,2});}
	    		if (s[2][1]=='1'){ans++;v.push_back({2,1});}
	    		if (s[n-1][n]=='0'){ans++;v.push_back({n-1,n});}
	    		if (s[n][n-1]=='0'){ans++;v.push_back({n,n-1});}
    		}
    	}
    	printf("%d\n",ans);
    	for (auto u:v)printf("%d %d\n",u.x,u.y);
    }
    return 0;
}

C.Palindromifier
题意:给定一个字符串,有两种操作,第一种是你选择2-i的子串,将其复制一遍然后翻转接到原串前面,第二种是你选择i-(n-1)的子串,将其复制一遍然后翻转接到原串后面。让你给出在30次操作以内将这个字符串变成回文串的操作方法。(2<=i<=n-1)
题解:
构造题就在纸上画画瞎搞(不是
手玩一会发现,如果第n-2个元素等于第n个元素,那我把2-n-1翻过去,然后把当前的第二个元素放到后面就成功了。那就是构造出第n-2个元素等于第n个了,这个简单 直接把当前的第n-1个翻到后面就完成了。

int main() {
    cin>>s;
    n=s.size();
    puts("3");
    printf("R %d\n",n-1);
    printf("L %d\n",n);
    printf("L %d\n",2);
    return 0;
}

D.Hexagons
题意:
给你一个蜂窝图,你每次可以往6个方向走,每次走的花费是给定的,问你从(0,0)到(n,m)的最小花费
题解:
一个方向的走法可以由另外两个操作完成可能是更优的,所以我们先处理出这个走法的最小花费。
然后我们根据6个方向将整个平面划分成6个象限。每个象限里仅用两种操作,这样你就发现走法是唯一的,直接统计一下就行。(因为我初始已经把一小步用别的一小步尝试优化了一下,所以已经没必要绕一下走了)
ps.盯着图一个个推怎么写真的费眼

int _;
ll x,y;
ll c[10],a[10];
int main() {
    for(scanf("%d",&_);_;_--){
        x=rd();y=rd();
        for (int i=1;i<=6;i++)a[i]=rd();
        c[1]=min(a[1],a[2]+a[6]);
    	c[2]=min(a[2],a[1]+a[3]);
    	c[3]=min(a[3],a[2]+a[4]);
    	c[4]=min(a[4],a[3]+a[5]);
    	c[5]=min(a[5],a[6]+a[4]);
    	c[6]=min(a[6],a[1]+a[5]);
    	if (x>=0&&y>=0&&y>=x){
    		printf("%lld\n",c[1]*x+c[2]*(y-x));
    	}
    	else if (x<=0&&y>=0){
    		printf("%lld\n",c[3]*(-x)+c[2]*y);
    	}
    	else if (x>=0&&y<=x&&y>=0){
    		printf("%lld\n",c[1]*y+(x-y)*c[6]);
    	}
    	else if (x>=0&&y<=0){
    		printf("%lld\n",c[6]*x+c[5]*(-y));
    	}
    	else if (x<=0&&y<=x){
    		printf("%lld\n",(-x)*c[4]+(x-y)*c[5]);
    	}
    	else{
    		printf("%lld\n",(-y)*c[4]+(y-x)*c[3]);
    	}
    }
    return 0;
}

E.Swedish Heroes
题意:给定一个数列,你每次选择a[i]a[i]a[i]a[i+1]a[i+1]a[i+1]删掉然后放回−(a[i]+a[i+1])-(a[i]+a[i+1])(a[i]+a[i+1])n−1n-1n1次操作后,留下的数最大是多少
题解:
对于这道题我们可以等价的看作在每一个数前面加上符号最后求和。
这道题的做法有一个归纳性的结论:设数列的长度为nnn,负号的数量为mmm,那么(n+m)(n+m)(n+m)%3=13=13=1.并且满足你的符号放置不能以+ - + - + -这样交替放置。
证明过程:这里采用数学归纳法的思想来证明。
n=1n=1n=1时,m=0m=0m=0(n+m)(n+m)(n+m)%3=13=13=1
n=2n=2n=2时,m=2m=2m=2(n+m)(n+m)(n+m)%3=13=13=1.
那么一步操作时,假设左边的数共有n1n1n1个,负号有m1m1m1个,右边的数有n2n2n2个,负号有m2m2m2个.此时满足(n1+m1)(n1+m1)(n1+m1)%3=1,(n2+m2)3=1,(n2+m2)3=1,(n2+m2)%3=13=13=1
操作后,n=n1+n2,m=(n1−m1)+(n2−m2)−>(n+m)n=n1+n2,m=(n1-m1)+(n2-m2)->(n+m)n=n1+n2,m=(n1m1)+(n2m2)>(n+m)%3=13=13=1
所以我们只需要最大化(n+m)(n+m)(n+m)%3=13=13=1的情况下,所有数和最大。
至于为什么不能+ - + -,我们在第一步操作后选中a[i]和a[i+1]他们在后续的操作中符号一定是相同的,不可能符号相反。
至此证毕。
具体的实现考虑dp的处理。
dp[i][j][0]dp[i][j][0]dp[i][j][0]代表处理到i位,(i+m)(i+m)(i+m)%3=j3=j3=j,尚未出现连续相同符号的1−i1-i1i的最大和

dp[i][j][1]dp[i][j][1]dp[i][j][1]代表处理到i位,(i+m)(i+m)(i+m)%3=j3=j3=j,已经出现连续相同符号的1−i1-i1i的最大和

每次dp[i]dp[i]dp[i]可以从dp[i−2]dp[i-2]dp[i2]转移过来,通过控制第iii个元素和第i−1i-1i1个前的符号实现转移。
同时,如果dp[i−1][j][1]dp[i-1][j][1]dp[i1][j][1]有值,也可以直接转移到dp[i]dp[i]dp[i].

int n,a[maxn];
ll dp[maxn][3][2];
int change[4][2]={1,1,-1,-1,1,-1,-1,1};
int num[4]={2,4,3,3};
int main() {
    n=rd();
    for (int i=1;i<=n;i++)a[i]=rd();
    if (n==1){printf("%d\n",a[1]);return 0;}
    for (int i=0;i<=n;i++)for (int j=0;j<3;j++)for (int k=0;k<2;k++)dp[i][j][k]=-inf;
    dp[0][0][0]=0;
	dp[1][1][0]=a[1];
	dp[1][2][0]=-a[1];
    for (int i=2;i<=n;i++){
    	for (int j=0;j<3;j++){
			for (int d=0;d<4;d++){
				int f=a[i-1]*change[d][0],g=a[i]*change[d][1];
				for (int k=0;k<2;k++)
					if (dp[i-2][j][k]!=-inf){
						if (d<2)
							dp[i][(j+num[d])%3][1]=max(dp[i][(j+num[d])%3][1],dp[i-2][j][k]+f+g);
						else
							dp[i][(j+num[d])%3][k]=max(dp[i][(j+num[d])%3][k],dp[i-2][j][k]+f+g);
					}
			}
			if (dp[i-1][j][1]!=-inf){
				dp[i][(j+1)%3][1]=max(dp[i][(j+1)%3][1],dp[i-1][j][1]+a[i]);
				dp[i][(j+2)%3][1]=max(dp[i][(j+2)%3][1],dp[i-1][j][1]-a[i]);
			}
    	}
    }
    cout<<dp[n][1][1]<<endl;
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值