文章目录
A - 咕咕东的奇遇
题目描述
咕咕东是个贪玩的孩子,有一天,他从上古遗迹中得到了一个神奇的圆环。这个圆环由字母表组成首尾相接的环,环上有一个指针,最初指向字母a。咕咕东每次可以顺时针或者逆时针旋转一格。例如,a顺时针旋转到z,逆时针旋转到b。咕咕东手里有一个字符串,但是他太笨了,所以他来请求你的帮助,问最少需要转多少次。
input
输入只有一行,是一个字符串。
Output
输出最少要转的次数。
Sample Input
zeus
Sample Output
18
题解
- 考虑到从一个字符到下一个字符只有两种方式,一种是逆时针,一种是顺时针,于是思路是,比较一下两种方式哪一种距离最近就用哪一种,最终状态是一样的。
- 通过取模运算来获取两种方式的距离,给
a…z
编号为0…25
先比较一下当前位置和目标位置的编号大小,令小的为minn
,大的为maxx
,则两个距离分别是minn+26-maxx
和maxx-minn
,将最小者加入到ans里即可。
代码
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
#include <algorithm>
#include <cmath>
using namespace std;
int main() {
string s;
cin >> s;
int len = s.length();
int sum = 0, cur = 0;
for(int i = 0; i < len; ++ i) {
int x = s[i]-'a';
int minn = min(x,cur), maxx = max(x,cur);
int step = min(minn+26-maxx, maxx-minn);
cur = x;
sum += step;
// cout << step << endl;
}
cout << sum << endl;
return 0;
}
B - 咕咕东想吃饭
题目描述
咕咕东考试周开始了,考试周一共有n天。他不想考试周这么累,于是打算每天都吃顿好的。他决定每天都吃生煎,咕咕东每天需要买ai个生煎。
但是生煎店为了刺激消费,只有两种购买方式:
①在某一天一次性买两个生煎。
②今天买一个生煎,同时为明天买一个生煎,店家会给一个券,第二天用券来拿。
没有其余的购买方式,这两种购买方式可以用无数次,但是咕咕东是个节俭的好孩子,他训练结束就走了,不允许训练结束时手里有券。咕咕东非常有钱,你不需要担心咕咕东没钱,但是咕咕东太笨了,他想问你他能否在考试周每天都能恰好买ai个生煎。
input
输入两行,第一行输入一个正整数n(1<=n<=100000),表示考试周的天数。
第二行有n个数,第i个数ai(0<=ai<=10000)表示第i天咕咕东要买的生煎的数量。
Output
如果可以满足咕咕东奇怪的要求,输出"YES",如果不能满足,输出“NO”。(输出不带引号)这里是引用
Sample Input1
4
1 2 1 2
Sample Output1
YES
Sample Input2
3
1 0 1
Sample Output2
NO
题解
- 考虑到最终要求判断的结果是最后一天有没有券的剩余,我们可以先假定最后一天满足条件,那么在这个前提下,前一天的购买状态我们也一定能获得,所以从最后一天往前推,知道推到第一天,让第一天也满足条件,最终检查第0天(实际不存在的天)是否是0,即是否没有溢出,即可获取答案。
- 嗷还有一件事,我们分析题目要求可以得知,在前一天和后一天同时消耗2次以上券的状态,完全可以用分别购买一次2个生煎来代替,所以只需要考虑%2的结果即可。
代码
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
#include <algorithm>
#include <cmath>
using namespace std;
const int MAXN = 100010;
int days[MAXN];
int main() {
int n;
cin >> n;
for (int i = 1; i <= n; ++i) {
scanf("%d", &days[i]);
}
days[0] = 0;
for (int i = n; i >= 1; i--) {
int x = days[i]%2; // if single digit
if (x) { // if next day need my day
if (days[i-1]) {
days[i-1] --;
} else {
puts("NO");
exit(0);
}
}
}
// cout << days[0] << endl;
if(days[0] != 0) {
puts("NO");
} else {
puts("YES");
}
return 0;
}
/*
4
1 2 2 2
*/
C - 可怕的宇宙射线
题目描述
众所周知,瑞神已经达到了CS本科生的天花板,但殊不知天外有天,人外有苟。在浩瀚的宇宙中,存在着-种叫做苟狗的生物, 这种生物天生就能达到人类研究生的知识水平,并且天生擅长CSP,甚至有全国第一的水平!但最可怕的是,它可以发出宇宙射线!宇宙射线可以摧毁人的智商,进行降智打击!
宇宙射线会在无限的二维平面上传播(可以看做一个二维网格图),初始方向默认向上。宇宙射线会在发射出一段距离后分裂,向该方向的左右45°方向分裂出两条宇宙射线,同时威力不变!宇宙射线会分裂n次,每次分裂后会在分裂方向前进a个单位长度。
现在瑞神要带着他的小弟们挑战苟狗,但是瑞神不想让自己的智商降到普通本科生zjm那么菜的水平,所以瑞神来请求你帮他计算出共有多少个位置会被"降智打击”。
input
输入第一行包含一个正整数n,(n<=30)n,(n<=30) n,(n<=30)n,(n<=30),表示宇宙射线会分裂n次。
第二行包含n个正整数a1,a2,⋯,ana1,a2,⋯,an a_1,a_2,\cdots,a_na1,a2,⋯,an,第ii ii个数ai(ai≤5)ai(ai≤5) a_i(a_i\le 5)ai(ai≤5)表示第ii ii次分裂的宇宙射线会在它原方向上继续走多少个单位长度。
Output
输出一个数ans,表示有多少个位置会被降智打击。
Sample Input
4
4 2 2 3
Sample Output
39
题解
- 首先分析可「降智」的范围:如果只朝一个方向走的话,
30*5
也只有150,所以一个方向走的最远距离便是150,为了不让我们的坐标为负,我们可以定义300*300
的棋盘,从(150,150)
开始dfs。 - 对于dfs,朴素铁定超时,但是我们可以考虑,在这么小的棋盘范围中,可能会有重复的状态发生,即到了某个点,这时的分裂次数,方向都是相同的,那么我们完全不用再来一遍,因为结果肯定和之前是一样的。所以我们把当前的状态压缩进一个数组中
vis[300][300][MAXN][10];
分别代表x,y坐标,第几次分裂,分裂方向。这样每次进行标记和验证就可以了。 - 还要有一个记录数组,记录的是本点是否被走过。标记的同时加入到 ans 答案计数器里最后输出即可。
- 注意这里不能考虑的太细,比如标记在一个方向进行中的状态,虽然时限会再减小,但是太难写了写不出来就像是考试的时候一样TAT。
- 还有一些小细节比如,左转和右转就可以用取模运算来解决。
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
using namespace std;
typedef long long LL;
const int MAXN = 35;
int dx[] = {0,1,1,1,0,-1,-1,-1};
int dy[] = {1,1,0,-1,-1,-1,0,1};
int n;
LL ans = 0;
int a[MAXN];
int vis[300][300][MAXN][10];
int ansvis[300][300];
void dfs(int x, int y, int cnt, int dir) {
if(cnt > n) return;
if(vis[x][y][cnt][dir]) return; // zhuangtai fangwenguo
vis[x][y][cnt][dir] = true;
for(int i = 1; i <= a[cnt]; ++i) {
x += dx[dir];
y += dy[dir];
if(!ansvis[x][y]) {
ansvis[x][y] = true;
ans ++;
}
}
dfs(x, y, cnt+1, (dir+1)%8); // right
dfs(x, y, cnt+1, (dir+7)%8); // left
}
int main() {
memset(vis, 0, sizeof(vis));
memset(ansvis, 0, sizeof(ansvis));
cin >> n;
for (int i = 1; i <= n; ++i) {
scanf("%d", &a[i]);
}
dfs(150,150,1,0);
cout << ans << endl;
return 0;
}
/*
4
4 2 2 3
*/