A、Three Friends
【题意】
在直线上有三个点 a、b、c,每个点都可以向左或者向右移动1个单位,问 |a−b|+|a−c|+|b−c| 最小值是多少。
【思路、解题过程及感想】
首先将a、b、c排列(这里我直接装进数组用的sort),然后进行判断。排序后a >= b >= c,那么|a−b|+|a−c|+|b−c|就可以化成2×(a - c),最小的数就是a == c的情况,为0。首先判断a、b、c三个数想不相等,如果相等就可以直接输出0,不相等的话a–,c++,因为要使2×(a - c)最小,那么就要a减小c增大,使区间范围变小(当b与a或者c相等时可以跟随着a或者c移动),但这里要再进行一次判断,如果变化后a>c的话那么没有影响,输出2×(a - c);如果a<=c的话,那么说明a与c之间的间距小于等于2,经过移动后可以相等,输出0。
这道题做的时候wa了一次,因为判断条件过于复杂,判断a和c改变值用了多组if、else,有些条件没有考虑到导致的错误,最后再次细致的观察演算了一下,才简化改进了一下ac。
做的时候以及过了半个多小时还没ac一道题,因为练习时题目难度并不是按照顺序排列的,前面做一道题卡住了,换了这道题之后有些着急了,然后没有细致考虑就直接写的代码,wa了一遍,所以说静下心来解题很重要。
【代码】
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define ll long long
using namespace std;
int main ()
{
ll t,a[5];
cin >> t ;
while(t--)
{
for(int i=0; i<3; i++)
cin >> a[i];
sort(a,a+3);
if (a[0]==a[1]&&a[1]==a[2])
cout << 0 <<endl;
else
{
a[0]++;
a[2]--;
if(a[2]-a[0]>0)
cout << 2*(a[2]-a[0]) <<endl;
else
cout << 0 <<endl;
}
}
return 0;
}
B、Snow Walking Robot
【题意】
给出一段机器指令,你可以删去一些指令,使最后剩余的指令操作之后回到原点,且这个过程中不能经过相同点,问最多可以剩下多少指令。
其实就是在原来指令基础上重新排列指令,让其从原点出发绕一圈回到原点。
【思路、解题过程及感想】
这个题有几点需要注意的,第一是可以删除指令,可以重新排列指令,第二是除了起点之外不能在同一个点经过两次。基于第一点与题意可以推断出,L和R数量应该相等,U和D数量应该相等,若不等则都置为每组最小的那个数;基于第二点可以推断出,假如{L、R}和{U、D}这两组中,有一组中有0,那么另一组就只能走两步,出去,回来,多走的话回来的时候就会重复经过同一个点。再就是让他的路径成为一个正方形就可以了。
这道题是补的,ACD三道题用了挺长时间,且这道题题目有点长,看起来有点麻烦,最后理解错意思了,也没出来代码,但真的回头来仔细看一看这道题目,其实也不难,理解题意之后就很好解决了。
【代码】
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
#define ll long long
int main()
{
int t;
cin >> t;
string s;
while(t--)
{
int l=0,r=0,u=0,d=0;
cin >> s;
int len=s.length();
for(int i=0;i<len;i++)
{
if(s[i]=='L') l++;
else if(s[i]=='R') r++;
else if(s[i]=='U') u++;
else if(s[i]=='D') d++;
}
int x=min(l,r),y=min(u,d);
if(x&&y)
{
cout << ((x<<1) + (y<<1)) << endl;
for(int i=0;i<x;i++) cout << 'L';
for(int i=0;i<y;i++) cout << 'U';
for(int i=0;i<x;i++) cout << 'R';
for(int i=0;i<y;i++) cout << 'D';
cout << endl;
}
else
{
if(x) cout << 2 << endl << "LR" << endl;
else if(y) cout << 2 << endl << "UD" << endl;
else cout << 0 << endl;
}
}
return 0;
}
C、Yet Another Broke Keyboard
【题意】
即使找了翻译这个题也是半半卡卡的,但根据样例还是摸索出来题意了。
给定一个字母序列和几个字母,然后找出含给出字母的连续子串。
其实真正让你做的就是把给出的字母序列分割成若干段,用的是第二行没有给出的字母来分割,例如:
9 3
abdradara
a b d
这个例子是把序列分成 abd | ada | a 三段,然后求每段子串个数个计算公式题目中给了,设n为一段的长度,其子串的个数就是n(n+1)/2,最后把每一段的加起来就可以了。
【思路、解题过程及感想】
思路就像是题意中写的一样,首先在输入字母的时候用一个数组来记录一下作为判断,然后分段并计算每一段的字串,累加即可。再具体参考代码。
【代码】
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define ll long long
using namespace std;
const int maxn=2e5+10;
ll str[maxn];
int main ()
{
ll n,k,ans,num;string a;
cin >> n >> k >> a;
char b;
for(int i=0;i<k;i++)
{
cin >> b;
str[b] = 1;//记录
}
num = ans = 0;
for(int i=0;i<n;i++)
{
if(str[a[i]])
num++;
else{
ans += num*(num+1)/2;
num = 0;
}
}
//这里注意如果最后一个字母也被记录,那么最后一段的子串种类就没有加上,所以最后要再加一次。
cout << ans+num*(num+1)/2 <<endl;
return 0;
}
D、Remove One Element
【题意】
给出一个序列,最多可以删除一个字符,求最长连续上升子串。
【思路、解题过程及感想】
思路其实很简单,两个dp数组,一个从前向后记录,一个从后向前记录,然后在序列中找最长的子串即可。
一开始走了一些弯路,用的一遍dp,但是判断越多越容易出现漏洞,越容易出现问题,且数组开小了,就出了好多问题。
一开始有的想法就是拼接,后来想到了以前做过有些类似的题目,就想到了两个数组两个方向记录然后拼接最后ac。
【代码】
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define ll long long
using namespace std;
const int maxn=2e5+10;
ll a[maxn];
ll b[maxn],c[maxn];
int main ()
{
int n;
ll maxx = -1;
cin >> n;
for(int i=0; i<n; i++)
{
cin >> a[i];
b[i] = c[i] = 1;
}
for(int i=1; i<n; i++)
if(a[i]>a[i-1])
b[i]=b[i-1]+1;
for(int i=n-2; i>=0; i--)
if(a[i]<a[i+1])
c[i]=c[i+1]+1;
for(int i=0; i<n; i++)
{
if(a[i]<a[i+2])
maxx = max(maxx,b[i]+c[i+2]);
else
maxx = max(maxx,max(b[i],c[i]));
}
cout << maxx <<endl;
return 0;
}
E、Nearest Opposite Parity
【题意】
第i个元素可以跳到i+a[i]或i-a[i]的位置j(跳跃条件是不能跳出边界),然后从j又能跳a[j]的距离来进行移动,求从i跳多少次才能到达原数组中与a[i]奇偶性不同的点,如果跳不到就输出-1。
【思路、解题过程及感想】
用bfs,注意点的编号和点的值的区别。这道题说用的反向建图,图论这个东西不是很懂,这也是需要补的一点,这个题也是找的大佬博客来解决的。
【代码】
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
const int maxn = 200005;
struct node
{
int val, x, y;
} a[maxn];
vector<int> v[maxn];
int main()
{
int n;
cin >> n;
for (int i = 1; i <= n; i++)
{
a[i].x = a[i].y = inf;
cin >> a[i].val;
if (i + a[i].val <= n)
v[i + a[i].val].push_back(i);
if (i - a[i].val >= 1)
v[i - a[i].val].push_back(i);
}
queue<int>q;
for (int i = 1; i <= n; i++)
{
q.push(i);
}
while (!q.empty())
{
int head = q.front();
q.pop();
for (auto i : v[head])//v[head]中放的是下一次移动能到达head位置的点的编号,即上一个点的编号
{
if (a[head].val & 1)//如果head点值为奇数
{
if (a[i].x > 1 || a[i].y > a[head].y + 1)//head点为奇数值,那么它上一个点到奇数值点的最短距离只有1一种可能。i的x值更新为1,而y记录的是head点到下一个y点的距离
{
a[i].x = 1;
if (a[i].y > a[head].y + 1)//位置i的点用对应的y继承位置head点到值为偶数的点的最短距离
a[i].y = a[head].y + 1;
q.push(i);
}
}
else
{
if (a[i].y > 1 || a[i].x > a[head].x + 1)
{
a[i].y = 1;
if (a[i].x > a[head].x + 1)
a[i].x = a[head].x + 1;
q.push(i);
}
}
}
}
for (int i = 1; i <= n; i++)
{
if (a[i].val & 1)
{
cout << (a[i].y == inf ? -1 : a[i].y) << " ";
}
else
cout << (a[i].x == inf ? -1 : a[i].x) << " ";
}
}
F、Two Bracket Sequences
【题意】
输入两个括号序列 s,t,你需要构造一个尽可能短的合法括号序列使得s,t 都是这个序列的子序列(子序列意味着不用连续)
【思路、解题过程及感想】
这道题是真的想不出来怎么解,找的题解说是是三维dp + 深搜,不怎么理解。
具体思路:设dp[i][j][v]表示s串已匹配 前i个括号,t串 已经匹配前j个括号,此时构造的括号序列左括号比右括号多 v 个的构造序列的最小长度。
转移:枚举每一位放 ( 还是放 )
放置 ( :若 s[i] == ( 则 ni = i + 1,同理 nj,dp[ni][ni][v + 1] = dp[i][j][v] + 1;
放置 ) :若 s[i] == ) 则 ni = i + 1,同理 nj,dp[ni][ni][v - 1] = dp[i][j][v] + 1;
要注意整个过程要保证 0 <= v <= 200,不在这个范围内一定不是正解
要构造最后的答案,需要记录转移的父节点。
看到这道题还以为是括号匹配问题,结果看了半天没看明白题意,看样例看的有点蒙,后来找题解发现了最后意思是构建新序列其子序列中有s和t。
欠缺还是很多。
大佬的题解博客
总结
相关知识漏洞太多了,需要补的知识有图论、树还有一些算法知识,都需要补充。
这个假期将会是很充实的一个假期。