「程序设计思维与实践」 CSP-M1:取模+模拟+dfs

A - 咕咕东的奇遇

题目描述

咕咕东是个贪玩的孩子,有一天,他从上古遗迹中得到了一个神奇的圆环。这个圆环由字母表组成首尾相接的环,环上有一个指针,最初指向字母a。咕咕东每次可以顺时针或者逆时针旋转一格。例如,a顺时针旋转到z,逆时针旋转到b。咕咕东手里有一个字符串,但是他太笨了,所以他来请求你的帮助,问最少需要转多少次。
在这里插入图片描述

input

输入只有一行,是一个字符串。

Output

输出最少要转的次数。

Sample Input
zeus
Sample Output
18
题解
  • 考虑到从一个字符到下一个字符只有两种方式,一种是逆时针,一种是顺时针,于是思路是,比较一下两种方式哪一种距离最近就用哪一种,最终状态是一样的。
  • 通过取模运算来获取两种方式的距离,给a…z编号为0…25先比较一下当前位置和目标位置的编号大小,令小的为minn,大的为maxx,则两个距离分别是minn+26-maxxmaxx-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

*/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值