Codeforces Round #605 (Div. 3) 题解
文章目录
A. Three Friends
-
题意
给定 a , b , c a,b,c a,b,c,可以让每个数最多操作一次 + 1 +1 +1或 − 1 -1 −1,问最小化得到的 ∣ a − b ∣ + ∣ a − c ∣ + ∣ b − c ∣ |a-b|+|a-c|+|b-c| ∣a−b∣+∣a−c∣+∣b−c∣值。 -
解题思路
贪心思考,我们可以对这 3 3 3个点进行排序,那么这样看我们易知,让这 3 3 3个点靠在一起最优。
所以我们分类讨论两者之间的大小关系,考虑是否进行操作。
当然,这道题也可以直接暴力枚举每个值的变化状态,然后取最小值输出即可。 -
AC代码
/**
*@filename:A
*@author: pursuit
*@created: 2021-08-21 14:53
**/
#include <bits/stdc++.h>
#define debug(a) cout << "debug : " << (#a)<< " = " << a << endl
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;
const int N = 1e5 + 10;
const int P = 1e9 + 7;
const int INF = 0x3f3f3f3f;
int t,a[3];
bool vis[N];
void solve(){
sort(a,a + 3);
if(a[1] > a[0]){
a[0] ++;
if(a[1] > a[0])a[1] --;
if(a[2] > a[0])a[2] --;
}
else if(a[1] == a[0]){
a[0] ++,a[1] ++;
if(a[2] > a[0])a[2] --;
else if(a[2] < a[0])a[2] ++;
}
int ans = abs(a[2] - a[1]) + abs(a[2] - a[0]) + abs(a[1] - a[0]);
printf("%d\n", ans);
}
int main(){
scanf("%d", &t);
while(t -- ){
for(int i = 0; i < 3; ++ i)scanf("%d", &a[i]);
solve();
}
return 0;
}
B. Snow Walking Robot
-
题意
给你一个指令顺序,从中删除最少的指令并重排使得机器人行走路线只会经过除 ( 0 , 0 ) (0,0) (0,0)的点一次,且最后回到起点。 -
解题思路
我们知道,如果要回到起点,那么我们所用的指令 c n t l = c n t r , c n t u = c n t d cnt_l=cnt_r,cnt_u=cnt_d cntl=cntr,cntu=cntd。我们再细致分析,如果 m i n ( L , R ) > 0 a n d m i n ( U , D ) > 0 min(L,R)>0 \ and\ min(U,D) > 0 min(L,R)>0 and min(U,D)>0,那么我们是可以利用其围成一个矩形的,这样能保证实现要求。否则,我们最多只能在 x x x或者 y y y轴上做移动,这个时候指令序列长度就最多只能为 2 2 2了,即踏出一步再返回。
至此,我们具体分析处理即可。 -
AC代码
/**
*@filename:B
*@author: pursuit
*@created: 2021-08-21 15:05
**/
#include <bits/stdc++.h>
#define debug(a) cout << "debug : " << (#a)<< " = " << a << endl
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;
const int N = 1e5 + 10;
const int P = 1e9 + 7;
const int INF = 0x3f3f3f3f;
int q,u,r,l,d;
string s;
/*
访问了除(0,0)单元格两次,就会中断。
有多少个R就要有多少个L,同理有多少个U就要有多少个D。
*/
void solve(){
for(int i = 0; i < s.size(); ++ i){
if(s[i] == 'U')u ++;
else if(s[i] == 'R')r ++;
else if(s[i] == 'L')l ++;
else d ++;
}
int minn1 = min(u,d),minn2 = min(l,r);
if(minn1 == 0 || minn2 == 0){
if(minn1 >= 1 || minn2 >= 1){
if(minn1 >= 1){
cout << 2 << '\n' << "UD" << endl;
}
else{
cout << 2 << '\n' << "LR" << endl;
}
}
else{
cout << 0 << endl;
}
}
else{
u = d = minn1,l = r = minn2;
cout << 2 * minn1 + 2 * minn2 << endl;
//绕个正方形。
for(int i = 0; i < u; ++ i){
cout << 'U';
}
for(int i = 0; i < l; ++ i){
cout << 'R';
}
for(int i = 0; i < d; ++ i){
cout << 'D';
}
for(int i = 0; i < r; ++ i){
cout << 'L';
}
cout << endl;
}
}
int main(){
cin >> q;
while(q -- ){
u = r = l = d = 0;
cin >> s;
solve();
}
return 0;
}
C. Yet Another Broken Keyboard
-
题意
给定不能敲打的字符和一个字符串 s s s,问能敲出多少个合法的子串。 -
解题思路
如果一个字符串是合法字符串,那么其所有子串都是合法的,不难得出如果一个合法字符串长度为 n n n,那么其所有合法子串的数量为 n × ( n + 1 ) 2 \frac{n\times(n + 1)}{2} 2n×(n+1)。所以我们可以找到最长的合法子串,然后计算统计即可,再跳到下一个直到遍历完字符串即可。这个过程可以用双指针实现。 -
AC代码
/**
*@filename:C
*@author: pursuit
*@created: 2021-08-21 15:26
**/
#include <bits/stdc++.h>
#define debug(a) cout << "debug : " << (#a)<< " = " << a << endl
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;
const int N = 2e5 + 10;
const int P = 1e9 + 7;
const int INF = 0x3f3f3f3f;
int n,k;
char s[N];
bool vis[200];
void solve(){
int l = 1,r = 1;
ll ans = 0;
while(l <= n){
while(r <= n && vis[s[r]])r ++;
//[l,r - 1]都是有效的。
ans += 1LL * (r - l + 1) * (r - l) / 2;
l = r;
if(!vis[s[l]])l ++;
r = l;
}
cout << ans << endl;
}
int main(){
cin >> n >> k >> s + 1;
char c;
for(int i = 0; i < k; ++ i){
cin >> c;
vis[c] = true;
}
solve();
return 0;
}
D. Remove One Element
-
题意
给定一个序列,你最多可以删除一个元素。问能产生的最长连续上升子序列长度是多少? -
解题思路
我们可以用 d p 1 [ i ] dp1[i] dp1[i]来表示最后一位元素为 a [ i ] a[i] a[i]的最长连续上升子序列长度, d p 2 [ i ] dp2[i] dp2[i]来表示最开始的元素为 a [ i ] a[i] a[i]的最长连续上升子序列长度。
那么当我们枚举删除元素的时候,我们即是判断前面子序列和后面子序列能否拼接在一起,即是判断 a [ i − 1 ] < a [ i + 1 ] a[i- 1]<a[i + 1] a[i−1]<a[i+1]。如果是相等的,那么不难发现构成的拼接连续子序列为 d p [ i − 1 ] + d p [ i + 1 dp[i-1]+dp[i+1 dp[i−1]+dp[i+1。枚举删除元素取最大值即可。 -
AC代码
/**
*@filename:D
*@author: pursuit
*@created: 2021-08-21 15:37
**/
#include <bits/stdc++.h>
#define debug(a) cout << "debug : " << (#a)<< " = " << a << endl
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;
const int N = 2e5 + 10;
const int P = 1e9 + 7;
const int INF = 0x3f3f3f3f;
int n,a[N],dp1[N],dp2[N];
void solve(){
for(int i = 2; i <= n; ++ i){
if(a[i] > a[i - 1]){
dp1[i] = dp1[i - 1] + 1;
}
}
for(int i = n - 1; i >= 1; -- i){
if(a[i] < a[i + 1]){
dp2[i] = dp2[i + 1] + 1;
}
}
//枚举删除的元素。
int maxx = 0;
for(int i = 1; i <= n; ++ i){
maxx = max(maxx,dp1[i]);
if(i >= 2 && i <= n - 1 && a[i - 1] < a[i + 1]){
//debug(i);
maxx = max(maxx,dp1[i - 1] + dp2[i + 1]);
}
}
printf("%d\n", maxx);
}
int main(){
scanf("%d", &n);
for(int i = 1; i <= n; ++ i){
scanf("%d", &a[i]);
dp1[i] = 1;
dp2[i] = 1;
}
solve();
return 0;
}
E. Nearest Opposite Parity
-
题意
给你一个序列 a a a,在 i i i位置上的元素能跳到 i − a [ i ] i-a[i] i−a[i]或者 i + a [ i ] i+a[i] i+a[i]上。求出每个元素跳到值奇偶不同的最小步骤。 -
解题思路
一开始想的是 d f s dfs dfs,即从每个点开始记忆化搜索处理。但随着好几发Wa之后才懵懂,我们 d f s dfs dfs是不能保证是最小路径的,同时,我们还很难处理环,即很容易出现TLE。这个时候就要想到 b f s bfs bfs求最短路了,由于我们 i i i是由 i + a [ i ] i+a[i] i+a[i]和 i − a [ i ] i-a[i] i−a[i]得到的,所以即可看成是从 i + a [ i ] , i − a [ i ] i+a[i],i-a[i] i+a[i],i−a[i]到 i i i的,我们建立边。然后由于我们是要找到奇偶性不同的,所以我们可以将所有奇数点和偶数点分开存放。然后从奇数点出发跑 b f s bfs bfs,得到到偶数点的最短路,同理同偶数点出发,得到到奇数点的最短路。
至此题解。 -
AC代码
/**
*@filename:E_New
*@author: pursuit
*@created: 2021-08-21 18:53
**/
#include <bits/stdc++.h>
#define debug(a) cout << "debug : " << (#a)<< " = " << a << endl
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;
const int N = 2e5 + 10;
const int P = 1e9 + 7;
const int INF = 0x3f3f3f3f;
//反向建边。
int n,a[N],dp[N][2];
struct edge{
int to,next;
}edges[N];
int head[N],tot;
vector<int> odd,even,ans;
void add(int u,int v){
edges[++ tot].to = v;
edges[tot].next = head[u];
head[u] = tot;
}
void bfs(vector<int> &st, vector<int> &ed){
vector<int> dp(n + 1,INF);//初始化。
queue<int> q;
for(auto &u : st)dp[u] = 0,q.push(u);
while(!q.empty()){
int u = q.front();
q.pop();
for(int i = head[u]; i; i = edges[i].next){
int v = edges[i].to;
if(dp[v] == INF){
dp[v] = dp[u] + 1;
q.push(v);
}
}
}
for(auto &v : ed){
if(dp[v] != INF){
ans[v] = dp[v];
}
}
}
void solve(){
ans.resize(n + 1, - 1);
bfs(odd,even);
bfs(even,odd);
for(int i = 1; i <= n; ++ i){
printf("%d ", ans[i]);
}
puts("");
}
int main(){
scanf("%d", &n);
for(int i = 1; i <= n; ++ i){
scanf("%d", &a[i]);
if(a[i] & 1)odd.push_back(i);
else even.push_back(i);
int idx1 = i - a[i], idx2 = i + a[i];
if(idx1 >= 1)add(idx1,i);
if(idx2 <= n)add(idx2,i);
}
solve();
return 0;
}
F. Two Bracket Sequences
-
题意
给定两个括号序列 s , t s,t s,t,求出最短的合法括号序列使得 s , t s,t s,t都是它的子序列。 -
解题思路
-
AC代码
我们可以用 f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k]来表示s串匹配到了前i个位置,t串匹配到了前j个位置,
且答案序列中的左括号比右括号多k个。这里需要注意,由于我们是一直向右添加,所以我们可以补救左括号。
易知起始状态为 f [ 0 ] [ 0 ] [ 0 ] f[0][0][0] f[0][0][0],终止状态为 f [ n ] [ m ] [ 0 ] f[n][m][0] f[n][m][0]。
我们再进一步考虑状态转移。
如果 s [ i ] = ′ ( ′ , t [ j ] = ′ ( ′ s[i] = '(', t[j] = '(' s[i]=′(′,t[j]=′(′,此时我们只需要放入一个’('即可。则 f [ i ] [ j ] [ k ] = f [ i − 1 ] [ j − 1 ] [ k − 1 ] + 1 f[i][j][k] = f[i - 1][j - 1][k - 1] + 1 f[i][j][k]=f[i−1][j−1][k−1]+1.
不过为了保证答案的合法性,所以我们还需要放入一个 ′ ) ′ ')' ′)′,则 f [ i ] [ j ] [ k ] = f [ i ] [ j ] [ k + 1 ] + 1 f[i][j][k] = f[i][j][k + 1] + 1 f[i][j][k]=f[i][j][k+1]+1。
如果 s [ i ] = ′ ) ′ , t [ j ] = ′ ( ′ s[i] = ')', t[j] = '(' s[i]=′)′,t[j]=′(′,此时我们需要放入一个左括号和一个右括号,且正好这样能维持平衡。
则 f [ i ] [ j ] [ k ] = f [ i − 1 ] [ j ] [ k + 1 ] + 1 , f [ i ] [ j ] [ k ] = f [ i ] [ j − 1 ] [ k − 1 ] + 1 f[i][j][k] = f[i - 1][j][k + 1] + 1,f[i][j][k] = f[i][j - 1][k - 1] + 1 f[i][j][k]=f[i−1][j][k+1]+1,f[i][j][k]=f[i][j−1][k−1]+1。
同理如果s[i] = ‘(’,t[j] = ‘)’,和第二种一样。
则 f [ i ] [ j ] [ k ] = f [ i − 1 ] [ j ] [ k − 1 ] + 1 , f [ i ] [ j ] [ k ] = f [ i ] [ j − 1 ] [ k + 1 ] + 1 f[i][j][k] = f[i - 1][j][k - 1] + 1,f[i][j][k] = f[i][j - 1][k + 1] + 1 f[i][j][k]=f[i−1][j][k−1]+1,f[i][j][k]=f[i][j−1][k+1]+1。
如果 s [ i ] = ′ ) ′ , t [ j ] = ′ ) ′ s[i] = ')',t[j] = ')' s[i]=′)′,t[j]=′)′,此时只需要放入一个 ′ ) ′ ')' ′)′即可,即 f [ i ] [ j ] [ k ] = f [ i − 1 ] [ j − 1 ] [ k + 1 ] + 1 f[i][j][k] = f[i - 1][j - 1][k + 1] + 1 f[i][j][k]=f[i−1][j−1][k+1]+1。
不过为了保证答案序列的合法性,所以我们还需要放入一个 ′ ( ′ '(' ′(′,则 f [ i ] [ j ] [ k ] = f [ i ] [ j ] [ k − 1 ] + 1 f[i][j][k] = f[i][j][k - 1] + 1 f[i][j][k]=f[i][j][k−1]+1。
至此,我们分析即得,利用 b f s bfs bfs搜索,每次枚举添加左括号和添加右括号得状态转移,利用 b f s bfs bfs能保证每次得到得都是最优解。不会做大量更新。
如果还是一头雾水,配代码使用更加,附有详细注释。 -
AC代码
/**
*@filename:F
*@author: pursuit
*@created: 2021-08-22 13:27
**/
#include <bits/stdc++.h>
#define debug(a) cout << "debug : " << (#a)<< " = " << a << endl
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;
const int N = 200 + 10;
const int P = 1e9 + 7;
const int INF = 0x3f3f3f3f;
int n,m;
char s[N],t[N];
struct node{
int x,y,z;
char c;//c表示该位置为左括号还是右括号。
node(int x = 0,int y = 0,int z = 0,char c = ' ') : x(x), y(y), z(z), c(c){}
}p[N][N][N];//表示dp的路径。为了我们输出解决方案。
vector<char> res;//结果串。
int f[N][N][N];//dp数组。
void bfs(){
//bfs寻找。
memset(f,-1,sizeof(f));
f[0][0][0] = 0;//初始状态值。
queue<node> q;
q.push(node());
node head;
while(!q.empty()){
head = q.front();
q.pop();
//为了保证序列的合法性,我们每次都要放入一对括号。
//这里添加左括号,判断是不是匹配成功了。
int nxtX = head.x + (head.x + 1 <= n && s[head.x + 1] == '(');
int nxtY = head.y + (head.y + 1 <= m && t[head.y + 1] == '(');
int nxtZ = head.z + 1;
if(nxtZ < N && f[nxtX][nxtY][nxtZ] == -1){
//由于我们是进行bfs,所以第一次放入一定是最小值。
f[nxtX][nxtY][nxtZ] = f[head.x][head.y][head.z] + 1;
p[nxtX][nxtY][nxtZ] = node(head.x,head.y,head.z,'(');//保存路径。
q.push(node(nxtX,nxtY,nxtZ));//再将该位置入队。
}
//这里添加右括号。判断是不是匹配成功了,如果匹配成功自然+1.
nxtX = head.x + (head.x + 1 <= n && s[head.x + 1] == ')');
nxtY = head.y + (head.y + 1 <= m && t[head.y + 1] == ')');
nxtZ = head.z - 1;
if(nxtZ >= 0 && f[nxtX][nxtY][nxtZ] == -1){
f[nxtX][nxtY][nxtZ] = f[head.x][head.y][head.z] + 1;
p[nxtX][nxtY][nxtZ] = node(head.x,head.y,head.z,')');//保存路径。
q.push(node(nxtX,nxtY,nxtZ));//再将该位置入队。
}
}
}
void dfs(node cur){
if(cur.x == 0 && cur.y == 0 && cur.z == 0){
printf("%c", cur.c);
return;
}
dfs(p[cur.x][cur.y][cur.z]);
printf("%c", cur.c);
}
void solve(){
n = strlen(s + 1), m = strlen(t + 1);
bfs();
dfs(p[n][m][0]);
}
int main(){
scanf("%s%s", s + 1, t + 1);
solve();
return 0;
}

浙公网安备 33010602011771号