程序设计思维与实践week4 CSP 补题三道

week4 周三下午13:30-15:30两小时的限时csp模拟,三题只做出了第一题。现复盘理清思路。

A - 咕咕东的奇遇

题目描述

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

输入格式

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

输出格式

输出最少要转的次数。

样例输入

zeus

样例输出

18

数据点说明
数据点字符串长度
1,2小于等于10
3,4,5小于等于100
6,7,8,9,10小于等于10000
示意图

在这里插入图片描述

分析

我是通过将各个char字符变成数字,即a为1,z为26等,分析后可以发现,两个数之间需要转的次数不会超过26/2=13,以a点和n点为方向,当两数之差<13时,通过n进行转动,次数即两数之差(n左边的数都大于右边的数),不然通过a进行转动,次数为26 - max(第一个点, 第二个点) + min(第一个点, 第二个点)。代码如下:

C语言

#include<algorithm>
#include <cstring>
#include<cstdio>
using namespace std;

int main() {
	char a[10000];
	int step = 0,first = 1;
	scanf("%s", &a);
	for (int i = 0;i < strlen(a); i++) {
		int x = a[i] - 96;//转换为对应的数字
		if (max(first, x)- min(first, x)>13)
			step += 26 - max(first, x) + min(first, x);
		else
			step += max(first, x) - min(first, x);
		first = x;//更新现在的位置为x
	}
	printf("%d",step);
	return 0;
}

B - 咕咕东想吃饭

题目描述

咕咕东考试周开始了,考试周一共有n天。他不想考试周这么累,于是打算每天都吃顿好的。他决定每天都吃生煎,咕咕东每天需要买ai个生煎。但是生煎店为了刺激消费,只有两种购买方式:①在某一天一次性买两个生煎。②今天买一个生煎,同时为明天买一个生煎,店家会给一个券,第二天用券来拿。没有其余的购买方式,这两种购买方式可以用无数次,但是咕咕东是个节俭的好孩子,他训练结束就走了,不允许训练结束时手里有券。咕咕东非常有钱,你不需要担心咕咕东没钱,但是咕咕东太笨了,他想问你他能否在考试周每天都能恰好买ai个生煎。

输入格式

输入两行,第一行输入一个正整数n(1<=n<=100000),表示考试周的天数。第二行有n个数,第i个数ai(0<=ai<=10000)表示第i天咕咕东要买的生煎的数量。

输出格式

如果可以满足咕咕东奇怪的要求,输出"YES",如果不能满足,输出“NO”。(输出不带引号)

样例输入 1

4
1 2 1 2

样例输出 1

YES

样例输入 2

3
1 0 1

样例输出 2

NO

数据点说明
数据点n(上限)ai(上限)
1,21010
3,4,5100010
6,71010000
8,9,1010000010000
分析

分析每天购买情况,不是奇数就是偶数,当为偶数时,要么是用第一种购买方式,一次买两个,此时手中没有券,券的状态不改变;要么是用第二种购买方式当天买了一个并用了昨天一张券,还有一张券留给明天,此时手中必有券,券的状态仍不改变。当购买数量为奇数时,要么是用第二种购买方式,买了一个留一张券给明天,此时券的状态改变,有无到有;要么是用第一种购买方法,买了两个再用掉了前一天留下的券,此时券的状态由有到无。
注意: 当a[i] == 0直接退出,输出NO,因为题目中有他决定每天都吃生煎

C++

#include<iostream>
using namespace std;
int a[100010];
int main() {
	int n;
	cin >> n;
	bool flag=false;//flase为没有票在手中
	for (int i = 0; i < n; i++) {
		cin >> a[i];
	}
	for (int i = 0; i < n; i++) {
		if (a[i]%2 == 0) {//为偶数时
			if (a[i] == 0) {
				flag = true;
				break;//退出
			}
			//else {
				//if (flag) { flag = true; continue; }//如果有票在手中,此时还是有票要给下一个
				//else { flag = false; }//如果没票在手中,此时为偶数,下一次还是没票
			//}
		}
		else {
			//if (flag)//如果有票在手中,下一天没票剩余了
			//	flag = false;
			//else//如果没票在手中,下一天必须得有票剩余
			//	flag = true;
			flag = (!flag);//综合后即这句话
		}
	}
	if (!flag)cout << "YES" << endl;
	else cout << "NO" << endl;

	return 0;
}

附上更加简洁高效的老师的代码。
在这里插入图片描述

C - 可怕的宇宙射线

时间与内存限制

每个测试点 1000ms 262144KB

题目描述

众所周知,瑞神已经达到了CS本科生的天花板,但殊不知天外有天,人外有苟。在浩瀚的宇宙中,存在着一种叫做苟狗的生物,这种生物天
生就能达到人类研究生的知识水平,并且天生擅长CSP,甚至有全国第一的水平!但最可怕的是,它可以发出宇宙射线!宇宙射线可以摧毁
人的智商,进行降智打击!
宇宙射线会在无限的二维平面上传播(可以看做一个二维网格图),初始方向默认向上。宇宙射线会在发射出一段距离后分裂,向该方向的
左右45°方向分裂出两条宇宙射线,同时威力不变!宇宙射线会分裂 次,每次分裂后会在分裂方向前进 个单位长度。
现在瑞神要带着他的小弟们挑战苟狗,但是瑞神不想让自己的智商降到普通本科生 那么菜的水平,所以瑞神来请求你帮他计算出共有多
少个位置会被"降智打击"

输入描述

输入第一行包含一个正整数n(n<=30) ,表示宇宙射线会分裂n次。
第二行包含n个正整数a1,a2……an ,第i个数ai(ai<=5)表示第i次分裂的宇宙射线会在它原方向上继续走多少个单位长度。

输出描述

输出一个数 ,表示有多少个位置会被降智打击

样例输入

4
4 2 2 3

样例输出

39

数据点说明
数据点n
10%<=10
40%<=20
100%<=30
示意图

下图描绘了样例中宇宙射线分裂的全过程,仅做参考。
在这里插入图片描述

分析

这题肯定要用到递归,每次在分裂到达的新的起点重新开始进行函数。但是如果递归不做任何处理,仅当到了分裂次数后退出,会超时。之前我用的就是二维数组,到过该点变为true,超时了。所以进行可行性剪枝,用到了四维数组,来记录起点的x,y坐标值,方向及第几次分裂,如果四项均相同,则是一样的情况,只要考虑一种情况就行(因为都到达同一个点只能算作一次),这是记忆化dfs,另外还可以用bfs来做。
至于为什么只要开300的数组,是因为30(最多分裂次数)*5(最多移动单位长度)*2(两边方向)= 300。

C++

#include<iostream>
using namespace std;
int n, ans=0;
int a[40];
bool flag[400][400] = { false };//记录网格中是否被标记过
bool check[400][400][35][8] = { false };//用四维数组记录情况,不让情况重复,否则容易超时
const int ddx[] = { 0,1,1,1,0,-1,-1,-1 };
const int ddy[] = { 1,1,0,-1,-1,-1,0,1 };
void dfs(int x,int y,int count, int pos)
{//x,y为坐标,count为现在进行的分裂次数,pos为方向
	if (count>n|| check[x][y][count][pos])
		return;//进行剪枝,达到了分裂次数后返回,同时如果起点相同,分裂次数相同,方向相同则为同一过程,则剪枝
	check[x][y][count][pos] = true;
	for (int i = 0; i < a[count]; i++)
	{//在指定移动长度内遍历符合条件的个数
		x += ddx[pos];
		y += ddy[pos];
		if (!flag[x][y])
		{//如果未被标记过
			ans++;
			flag[x][y] = true;
		}
	}
	dfs(x, y, count + 1, (pos + 7) % 8);//两个方向进行递归
	dfs(x, y, count + 1, (pos + 1) % 8);
}
int main()
{
	cin >> n;
	for (int i = 0; i < n; i++)
	{
		cin >> a[i];
	}
	dfs(200, 200, 1, 0);
	cout << ans << endl;
	return 0;
}

提供的代码差不多。
在这里插入图片描述

另外,有些收益匪浅的一些csp注意点。以后要多注意分情况考虑不同测试点,避免爆零!最后30分钟左右主要用来检查,减少不必要的丢分!要多多考虑复杂度的问题,容易分析到最后复杂度太高从而得从头重新开始思考解决方法。

在这里插入图片描述

对于超时问题的一些对策:

  • 先分析复杂度,如果复杂度正确则应优化常数,否则优化算法;
  • 常数优化:cin换scanf、替换stl、甚至考虑计算机原理层面的优化(局部性原理、手动实现递归等)。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值