算法-程序设计课week4---模拟题

A - 咕咕东的奇遇

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

Input
zeus
Output
18
思路

这道题挺简单,26个字母,对上一个字母与当前字母正反取距离,判断那一条路更近,将举例累加。

心得

从这道题开始我用起了一点宏定义技巧,真香。

代码
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define _for(i,s,e) for(int i=s;i<e;i++)

//旋转找最小总距离,其实只要每次旋转前判断顺逆时针那一个更近就好了
int main() {
	char str[10005];
	int total=0;

	scanf("%s",str);
	int len=strlen(str);

	char c='a';

	_for(i,0,len) {
		int minl=min(//寻找最短方向
		             26-abs(c-str[i]),
		             abs(c-str[i])
		         );
		total+=minl;
		c=str[i];//寻找下一个
	}

	printf("%d\n",total);
	return 0;
}

B - 咕咕东想吃饭

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

Input1
4
1 2 1 2
Output1
YES
Input2
3
1 0 1
Output2
NO
思路

我一开始是好几个嵌套if else,试图模仿所有情况,后来才发现原来这道题是有规律的:

  1. 每天用掉昨天的劵
  2. 若还剩偶数个生煎,可以用一次购买两个的方式将他们解决掉
  3. 若还剩奇数个生煎,则可以用多次买两个和一次买一煎一劵的方式

剩余券数=剩余生煎个数%2
这样的话题目完全不需要嵌套的逻辑判断,只需要进行简单的加减和取整。

心得
  1. 读题读题读题,圈起来圈起来圈起来。这道题我写了十几个测试用例,最后还是没有AC。后来忽然想通了,每天可以不止买一次!一开始我注意到两种购买方式可以用无数次了,但后面题目举的例子让我以为一天只能购买一次…读题什么的…
  2. 三道题里面,这道题最让我大开眼界,一个比较复杂的题目通过分析转化就变得很简单,amazing。
  3. 都说要先设计测试用例再编码,可这测试用例怎么设计呢?我绞尽脑汁设计了十几条测试用例,效率太低了,设计测试用例的套路是我要关心的一个点,应当开个笔记专门收集。
代码
#include <cstdio>
#define _for(i,s,e) for(int i=s;i<e;i++)
int a[100005];//大数据保存在全局区
int main() {
	int n; 
	scanf("%d",&n);
	bool ok = true;
	int last = 0;//券数量,只有1/0两种可能  
	_for(i,0,n) {
		scanf("%d",&a[i]);
		//如果某日没把昨天剩下的生煎劵吃光,那就失败了 
		if(a[i]-last<0) {
			ok = false;
			break;
		}
		//每天用掉昨天的劵,
		//若还剩偶数个生煎,可以用一次购买两个的方式将他们解决掉
		//若还剩奇数个生煎,则可以用多次买两个和一次买一煎一劵的方式
		//因此这里对2整除 
		last = (a[i]-last)%2;
	}
	
	//若过程中就失败,或过程顺利但最后一天剩劵了,则失败 
	if(last!=0 || ok==false){
		printf("NO\n");
	}else{//若每一天都成功且最后不剩券,则成功 
		printf("YES\n");
	}
	return 0;
}

C - 可怕的宇宙射线

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

Input
4
4 2 3 2
Output
39
思路

BFS+多条件剪枝+{1,0,-1}数组表示接下来的走向+set去重

  1. 主要使用BFS思想作为题目框架,每个点可延伸出两个方向,不要忘记每次步长是不确定的。
  2. 同点同方向同深度可以被剪枝,否则不能剪。
  3. 可以用整数标识方向,数组来获取特定方向上的dx,dy。一开始我用map来获取方向,性能低且可读性差。
  4. set去重。

这道题我一开始是单纯用BFS去做,当然并不能做出来。

后来知道了剪枝
再后来发现可以用数组表示走向(一开始是用map做的映射)
再后来发现全局数组开大也会MLE
小小题目蕴含大千世界😂

心得
  1. 周三下午课重叠,我得先验收完OS课设再去打比赛,所以做题只剩下一小时,所以只是匆匆做完前两题,第三题都没来得及研究。
    一半的时间拿到一半的分,应该还可以吧,虽然拿到的只是简单的那一半分(笑)
  2. 学习通过静态数组保存分支,这样挺方便。
  3. 一开始忘记走过的路径都可以被炸了,只写了炸终点的。
  4. 很关键的一点是要剪枝,暴力搞不定这道题目。
    1. 有种说法是对称剪枝,不过我觉得对称判断挺麻烦的(可能有什么奇淫技巧吧不过我没见过emmm)
    2. 我对 同位置同方向同层次进行剪枝 就可以AC,不同层次不可剪枝,因为不同层次步长不同。
    3. 刚碰到“剪枝”吓坏了,寻思这又是什么新知识,一看代码发现原来是个if(visited)return;
  5. 这道题要考虑的东西太多,脑子一团浆糊,做对了都理不太清思路(((m-__-)m
  6. 数组开大亿点点就MLE…,开小亿点点就AC,我又明白了,内存限制指的是总内存,而不单单指堆和栈。
代码
#include<bits/stdc++.h>
#define _for(i,s,e) for(int i=s;i<e;i++)
using namespace std;
struct point {
	int x;
	int y;
	bool operator<(const point& a) const {
		return x != a.x ? x < a.x : y < a.y;
	}
};

int n;
int a[50];
set<point> total;
bool visited[400][400][35][8]= {false};
int nextx[]= {0,1,1,1,0,-1,-1,-1};
int nexty[]= {1,1,0,-1,-1,-1,0,1};

void bfs(point p,int times,int dir) {

	if(times==n)return;

	int x=p.x;
	int y=p.y;

	//完全相同的访问则剪枝
	if(visited[p.x][p.y][times][dir])return;
	visited[p.x][p.y][times][dir]=true;

	//前进ai次
	for(int i=0; i<a[times]; i++) {
		total.insert({
			p.x+=nextx[dir],
			p.y+=nexty[dir]
		});
	}

	//广搜
	bfs({p.x,p.y},times+1,(dir+7)%8);
	bfs({p.x,p.y},times+1,(dir+1)%8);
}
int main() {
	scanf("%d",&n);
	_for(i,0,n) {
		scanf("%d",&a[i]);
	}
	bfs({200,200},0,0);
	printf("%d",total.size());
	return 0;
}

后记

我想起了一位老同学提到的“小圈子化”问题,圈内的人们疯狂玩梗,圈外人一脸懵逼。

就像算法竞赛,各种技巧和注意点,打算法竞赛的同学谈论起来游刃有余,可是圈外人就啥都听不懂,甚至看题解都懵。

我就纳闷了,二次元好歹还有个萌娘百科,算法竞赛的各种专有名词为啥没有个百科呢,每次遇到新东西只能在屈指可数的博文中寻找蛛丝马迹来理解它们。这知识领域有点…难洞悉啊,还好这门课能让我略微了解些皮毛。

经过寒假以及最近这段时间的学习,我发现算法这块没什么手册之类的东西,也没有什么全面的教材,唯一的学习方法就是刷题和看题解,甚至说看题解比刷题更重要。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值