Codeforces Round #605 (Div. 3) 题解

传送门

A. Three Friends

题意

给三个数 (a,b,c),每个数都可以 (+1,-1) 或不变,问对每一个数操作至多一次后 (|a'-b'|+|a'-c'|+|b'-c'|) 的最小值。

思路

暴力枚举每个数操作后的值,然后记录最小结果就行了

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int T,a[5];

int main(){
	scanf("%d",&T);
	while(T--){
		int a,b,c;
		scanf("%d%d%d",&a,&b,&c);
		LL res=0x3f3f3f3f3f3f3f;
		for(int i=-1;i<=1;i++)
			for(int j=-1;j<=1;j++)
				for(int k=-1;k<=1;k++){
					int x=a+i,y=b+j,z=c+k;
					res=min(res,1ll*abs(x-y)+abs(x-z)+abs(y-z));
				}
		printf("%lld
",res);
	}
	return 0;
}

B. Snow Walking Robot

题意

给一个字符串描述路径,要求从中删除尽量少的字符,使得从 ((0,0)) 沿着按照删改后的字符串走,可以走回 ((0,0)),并且除了 ((0,0)) 不会经过一个点两次

思路

统计'U'、'R'、'D'、'L'的个数,记作 (U,R,D,L)。从 ((0,0)) 出发,要走回 ((0,0)) 的话,那么显然 (U=D,R=L),那么首先需要使 (U=D=min(U,D),R=L=min(R,L)),然后按照先上,再右,再下,再左转一圈就可以走完。此时要注意,如果删改后 (U=D=0) 或者 (R=L=0),此时当然就只能走出一步然后马上回来,因为在这种情况下,只能原路返回,如果走多了,沿途的点也会被经过两次了,所以这种情况必须要特判掉。

代码

#include <bits/stdc++.h>
using namespace std;
const int MAXN=1e5+10;
int T;
char s[MAXN];

int main(){
	scanf("%d",&T);
	while(T--){
		scanf("%s",s+1);
		int n=strlen(s+1);
		int U=0,R=0,D=0,L=0;
		for(int i=1;i<=n;i++)
			if(s[i]=='U') U++;
			else if(s[i]=='R') R++;
			else if(s[i]=='D') D++;
			else if(s[i]=='L') L++;
		U=D=min(U,D);
		R=L=min(L,R);
		if(!U&&!D&&R&&L) L=R=1;
		if(!L&&!R&&U&&D) U=D=1;
		printf("%d
",U+D+L+R);
		for(int i=1;i<=U;i++) printf("U");
		for(int i=1;i<=R;i++) printf("R");
		for(int i=1;i<=D;i++) printf("D");
		for(int i=1;i<=L;i++) printf("L");
		printf("
");
	}
	return 0;
}

C. Yet Another Broken Keyboard

题意

给一个字符串 (s)(k) 个字母,问 (s) 的只包含这 (k) 种字母的子串个数。

思路

假设 (s[i,j]) 合法,那么它的子串一定合法,那么就会对答案产生贡献 (frac{(j-i+1)(j-i+2)}{2})
所以选出每一段合法区间,然后累加到答案上去就行了。

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MAXN=2e5+10;
int n,k,can[300];
char s[MAXN],temp[10];

int main(){
	scanf("%d%d",&n,&k);
	scanf("%s",s+1);
	for(int i=1,x;i<=k;i++){
		scanf("%s",temp);
		can[temp[0]]=1;
	}
	LL ans=0;
	int j=0;
	for(int i=1;i<=n;i++){
		if(can[s[i]]) j++;
		else{
			ans+=1ll*j*(j+1)/2;
			j=0;
		}
	}
	ans+=1ll*j*(j+1)/2;
	printf("%lld
",ans);
	return 0;
}

D. Remove One Element

题意

给一个序列,你可以从种删去至多一个数,问最长连续上升子序列的长度。

思路

(f[i][0/1]) 为以第 (i) 位结尾,未删除数/已删除数能得到的最长连续上升子序列的长度。
首先 (f[i][0]) 这个状态很好做,就是普通的最长连续上升子序列的长度:

[f[i][0]=f[i-1][0]+1 (a[i]>a[i-1])\ f[i][0]=1 (a[i]le a[i-1]) ]

(f[i][1]) 这个状态也不难,显然有两种情况会出现这种状态:
1.把 (a[i]) 接到以 (a[i-1]) 结尾且已删除数的最长上升连续子序列后。
2.把 (a[i]) 接到以 (a[i-2]) 结尾且未删除数的最长上升连续子序列后,显然这种情况是将 (a[i-1]) 删去了。

[f[i][1]=f[i-1][1]+1 (a[i]>a[i-1])\ f[i][1]=f[i-2][0]+1 (a[i]>a[i-2])\ f[i][1]=1 (任何情况下) ]

#include <bits/stdc++.h>
using namespace std;
const int MAXN=2e5+10;
int n,a[MAXN],f[MAXN][2];

int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	int ans=0;
	for(int i=1;i<=n;i++){
		f[i][0]=f[i][1]=1;
		if(a[i]>a[i-1]){
			f[i][0]=f[i-1][0]+1;
			f[i][1]=f[i-1][1]+1;
		} 
		if(a[i]>a[i-2]) f[i][1]=max(f[i][1],f[i-2][0]+1);
		ans=max(ans,max(f[i][0],f[i][1]));
	}
	printf("%d
",ans);
	return 0;
}

E. Nearest Opposite Parity

题意

给一个 (n) 个数的数列 (a_1,a_2,...,a_n),第 (i) 个数可以跳一次跳到第 (i'=i-a_i(i-a_igeq 1)) 个数或第 (i'=i+a_i(i+a_ileq n)) 个数上,问每个数最少跳几次,使得 (a_i)(a_{i'}) 的奇偶性不同。

思路

按理来说可以通过搜索来解决这道题的,但是直接搜索当然会超时,而我加上记忆化之后就 WA 了,所以我换了一种方法,建图之后一遍广搜可以解决。
说一下怎么建图
如果已知 (ans_i),那么如果 (j+a_j=i)(a_j\%2=a_i%2),那么 (ans_j=ans_i+1)
所以如果 (a_i\%2=a_{i+a_i}\%2),那么就可以建立一条 (i+a_i->i) 的有向边,
(i-a_i)(i) 同理
显然可以轻松的到 (ans_i=1) 的所有位置,那么可以从这些位置开始广搜即可

代码

#include <bits/stdc++.h>
using namespace std;
const int inf=0x3f3f3f3f;
const int MAXN=2e5+10;
int n,a[MAXN],ans[MAXN];
int head[MAXN],to[MAXN*2],nxt[MAXN*2],tot=0;
queue<int> que;

void add(int u,int v){
	to[++tot]=v;nxt[tot]=head[u];head[u]=tot;
}

int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	memset(ans,0x3f,sizeof(ans));
	for(int i=1;i<=n;i++){
		if(i-a[i]>=1&&a[i-a[i]]%2==a[i]%2) add(i-a[i],i);
		if(i+a[i]<=n&&a[i+a[i]]%2==a[i]%2) add(i+a[i],i);
	}
	for(int i=1;i<=n;i++)
		if(i-a[i]>=1&&a[i-a[i]]%2!=a[i]%2||i+a[i]<=n&&a[i+a[i]]%2!=a[i]%2)
			ans[i]=1,que.push(i);
	while(!que.empty()){
		int u=que.front();que.pop();
		for(int i=head[u];i;i=nxt[i])
			if(ans[to[i]]>ans[u]+1){
				ans[to[i]]=ans[u]+1;
				que.push(to[i]);
			}
	}
	for(int i=1;i<=n;i++) printf("%d ",ans[i]==inf?-1:ans[i]);
	return 0;
}

F. Two Bracket Sequences

题意

给两个括号序列 (s1,s2),要求构造一个最短的规范的括号序列 (ans),且满足 (s1,s2)(ans) 的子序列。

思路

广搜
假定已经有了一个构造了序列 ((i,j,k))
(i)(s1) 的前 (i) 位是目前序列的子序列
(j)(s2) 的前 (j) 位是目前序列的子序列
(k)(k=左括号-右括号)
那么就需要从 ((0,0,0)) 构造到 ((len_{s1},len_{s2},0))
如果将这个三元组看作是三维空间中的点的话,其实要求的就是从 ((0,0,0))((len_{s1},len_{s2},0)) 的最短路径。
因为这个从一个点到另一个点的距离就是 (1),那么求最短路可以直接广搜。
现在来看两个点之间的关系
已知括号序列 ((i,j,k))
1.如果向该序列添加一个 '(' (可以理解为点 ((i,j,k)) 向另一个点走)
如果 (s1[i+1]='(',s2[j+1]='('),可以构成序列 ((i+1,j+1,k+1)) (点 ((i,j,k)->(i+1,j+1,k+1)))
如果 (s1[i+1]='(',s2[j+1] ot='('),可以构成序列 ((i+1,j,k+1)) (点 ((i,j,k)->(i+1,j,k+1)))
如果 (s1[i+1] ot='(',s2[j+1]='('),可以构成序列 ((i,j+1,k+1)) (点 ((i,j,k)->(i,j+1,k+1)))
如果 (s1[i+1] ot='(',s2[j+1] ot='('),可以构成序列 ((i,j,k+1)) (点 ((i,j,k)->(i,j,k+1)))
2.如果向该序列添加一个 ')' (此时 (kgeq 0),因为要规范的括号序列,任意一个前缀中右括号都不能比左括号多)
如果 (s1[i+1]=')',s2[j+1]=')'),可以构成序列 ((i+1,j+1,k-1)) (点 ((i,j,k)->(i+1,j+1,k-1)))
如果 (s1[i+1]=')',s2[j+1] ot=')'),可以构成序列 ((i+1,j,k-1)) (点 ((i,j,k)->(i+1,j,k-1)))
如果 (s1[i+1] ot=')',s2[j+1]=')'),可以构成序列 ((i,j+1,k-1)) (点 ((i,j,k)->(i,j+1,k-1)))
如果 (s1[i+1] ot=')',s2[j+1] ot=')'),可以构成序列 ((i,j,k-1)) (点 ((i,j,k)->(i,j,k-1)))
那么以 ((0,0,0)) 为起点,进行一次宽搜。
记录下宽搜的路径,从终点 ((len_{s1},len_{s2},0)) 逆向构造出路径,就是答案了。

#include <bits/stdc++.h>
using namespace std;
char s1[210],s2[210];
int n,m;
struct  Node{
	int i,j,k;
}f[210][210][410];
queue<Node> que;

void bfs(){
	memset(f,-1,sizeof(f));
	f[0][0][0]=(Node){0,0,0};
	que.push((Node){0,0,0});
	while(!que.empty()){
		Node u=que.front();que.pop();
		int i=u.i,j=u.j,k=u.k,vi,vj,vk;
		vi=i+(s1[i+1]=='(');
		vj=j+(s2[j+1]=='(');
		vk=k+1;
		if(vk<=n+m&&f[vi][vj][vk].i==-1){
			f[vi][vj][vk]=u;
			que.push((Node){vi,vj,vk});
		}
		vi=i+(s1[i+1]==')');
		vj=j+(s2[j+1]==')');
		vk=k-1;
		if(vk>=0&&f[vi][vj][vk].i==-1){
			f[vi][vj][vk]=u;
			que.push((Node){vi,vj,vk});
		}
	}
}

int main(){
	scanf("%s%s",s1+1,s2+1);
	n=strlen(s1+1);
	m=strlen(s2+1);
	bfs();
	string ans;
	int i=n,j=m,k=0;
	while(i||j||k){
		Node u=f[i][j][k];
		if(k>u.k) ans="("+ans;
		else ans=")"+ans;
		i=u.i;j=u.j;k=u.k;
	}
	cout<<ans<<endl;
	return 0;
}
原文地址:https://www.cnblogs.com/BakaCirno/p/12034009.html