程序设计思维Week4-CSP模拟赛

程序设计思维Week4-CSP模拟赛

A-咕咕东的奇遇

Description

一个圆环上有一个指针,最初指向字母a。咕咕东每次可以顺时针或者逆时针旋转一格。例如,a顺时针旋转到z,逆时针旋转到b。咕咕东手里有一个字符串,但是他太笨了,所以他来请求你的帮助,问最少需要转多少次。
输入一行字符串,输出最少要转的次数。

在这里插入图片描述

Sample

input:
zeus

output:
18

Idea

首先计算字符串的长度即进行旋转的次数,从a开始,对于每个字母,有两个选择,从起始字符顺时针或逆时针旋转到该字母,选择路径最短的方法到达该字母,并相应更新下一次旋转的起始字母。最后累加得到最少要转的格数。

Summary

这道题较简单,主要要测量对顺时针和逆时针要旋转的格数,然后每次要记得更新起始字母

Codes

#include <iostream>
#include <string>
#include <cmath>
#include<cstdlib>
#include<cstdio>
using namespace std;

int main()
{
	cin.sync_with_stdio(false);
	string str;
	cin >> str;
	int size = str.length();
	int sum = 0;
	char now = 'a';
	for (int i = 0; i < size; i++) {
		int left = abs(str[i] - now);
		int right = 26-left;
		sum += left < right ? left : right;
		now = str[i];
	}
	printf("%d\n", sum);
}


B-咕咕东想吃饭

Description

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

Sample

input:
4
1 2 1 2

output:
YES

input:
3
1 0 1

output:
NO

Idea

因为每一天如果选择了第二种方案第二天必须把积累的票用完,所以用ticket记录前一天积累的票子,当一天要买的数量比前一天积累的票子数量少时宣告购买失败,否则扣除前一天的票子后采用第二种方案直至只剩下一个需购买的生煎或没有需购买的生煎了。循环之后判断最后一天结束时票子是否清零。

Summary

这道题事实上也不难,有几个要注意的:
①1<=n<=100000,要仔细读题,注意变量范围。
②一开始我陷入了递归的死胡同,以为第一种方案和第二种方案都可以采用零次或多次,这会产生很多情况。其实每天最多只用一次第二种方案,不必考虑多次采用第二种方案的情况,因为都可以用第一种方案替代,这样就简单很多了,ticket的值只有0或1。

Codes

#include <iostream>
#include<cstdlib>
#include<cstdio>
using namespace std;

int a[100000];
int n;
bool flag = true;

int main()
{
	cin.sync_with_stdio(false);
	cin >> n;
	for (int i = 0; i < n; i++)
		cin >> a[i];
	int ticket = 0;
	for (int i = 0; i < n; i++)
	{
		if (a[i] - ticket < 0) { flag = false; break; }
		ticket = (a[i] - ticket) % 2;

	}
	if (ticket != 0)flag = false;
	if (flag)cout << "YES";
	else cout << "NO";


}


C-可怕的宇宙射线

Description

宇宙射线会在无限的二维平面上传播(可以看做一个二维网格图),初始方向默认向上。宇宙射线会在发射出一段距离后分裂,向该方向的
左右45°方向分裂出两条宇宙射线,同时威力不变!宇宙射线会分裂n次,每次分裂后会在分裂方向前进ai个单位长度。
计算出共有多少个位置会被"降智打击"
输入宇宙射线会分裂的次数n(n<=30),以及第i次分裂的宇宙射线会在它原方向上继续走长度ai(ai<=5)。

在这里插入图片描述

Sample

input:
4
4 2 2 3

output:
39

Idea

这是迷宫问题的变形,可以使用DFS或BFS解决。射线在n次分裂时可能存在多个位置重合,所以需要去重地计算共有多少个位置被降智打击,这里我们使用set数据结构保证每个位置只在集合中出现一次。射线的分裂使运动方向存在8种情况即可以横竖斜着走,用数组存储运动的情况。
我们使用DFS递归到最后一次分裂从最后一条分裂的路径向前搜索,对于每次分裂,根据参数dir确定分裂方向的轴,当dir等于0或4时代表两条分裂出去的射线关于y轴对称,当dir等于1或5时代表关于直线y=x对称,当dir等于2或6时代表关于x轴对称,当dir等于3或7时代表关于直线y=-x对称,首先将其中一条对称路径的点加入set,已知对称轴和其中一条对称的射线,可以根据垂直平分线的性质推导出另一条对称射线上的点加入set,这样一直向前推可以获得所有到达的位置,最后集合大小就是有多少个位置被降智打击。

Summary

这是典型的迷宫问题的变形,关键在于记忆化分裂路径即对位置去重,与之前那道迷宫问题不同的是这里有8种运动方向,总的来说更难。
一开始我企图使用BFS解题,但几次剪枝后还是超时:

int bfs() {
	Q.push(point(0,0,0));
	while (!Q.empty()) {
		point now = Q.front();
		Q.pop();
		point temp;
		for (int i = 1; i <= a[index]; i++)
		{
			//cout << now.x + dx[now.dir] * i << " " << now.y + dy[now.dir] * i << endl;
			P.push_back(point2(now.x + dx[now.dir] * i, now.y + dy[now.dir] * i));
		}

		temp.x = now.x + dx[now.dir] * a[index];
		temp.y = now.y + dy[now.dir] * a[index];
		temp.dir = now.dir;
		

		sort(P.begin(), P.end(), cmp);

		int u = 0, v = 1;
		while (v < P.size()) {
			if (P[u] == P[v]) {  P.erase(P.begin() + v); }
			else { u = v; v = u + 1; }
		}
		if (index == 0)index++;
		else { 
			k++;
			if (pow(2, index+1) == k)index++;
		}
		
		if (index == n)return P.size();

		int d1 = temp.dir+1;
		int d2 = temp.dir - 1;
		if (d1 == 8)d1 = 0;
		if (d2 == -1)d2 = 7;
		Q.push(point(temp.x, temp.y, d1));
		Q.push(point(temp.x, temp.y, d2));
	}

}

之后我就使用DFS记忆化,可以想成从最后一次分裂的一个分裂点开始,已知对称轴和一个分裂的射线,可以用垂直平分线的对称性质推导出另一条分裂的射线,再向前推从上一层开始进行对称复制,直至复制出整张图。

Codes

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<set>
using namespace std;
struct point {
	int x, y;
	bool operator <(const point & p) const {
		if (x != p.x)return x < p.x;
		else return y < p.y;
	}
};
set<point> P;
int n;
int a[31];

int dx[] = { 0,1,1,1,0,-1,-1,-1 };
int dy[] = { 1,1,0,-1,-1,-1,0,1 };

void dfs(int x, int y, int index,int dir) {
	if (index > n) return;
	
	 
	dfs(x + dx[dir] * a[index], y + dy[dir] * a[index], index + 1 ,(dir + 1) % 8);
	//对称复制
	set<point> temp;
	for (auto &Q : P) {
		if (dir % 4 == 0) {
			temp.insert({ 2 * x - Q.x,Q.y });
		}
		else if (dir % 4 == 1) {
			temp.insert({ x + Q.y - y,y + Q.x - x });
		}
		else if (dir % 4 == 2) {
			temp.insert({Q.x,2 * y -Q.y });
		}
		else if (dir % 4 == 3) {
			temp.insert({ x + y - Q.y,y - Q.x + x });
		}
	}
	P.insert(temp.begin(), temp.end());
	
	for (int i = 0; i < a[index]; i++) {
		x = x + dx[dir];
		y = y + dy[dir];
		P.insert({ x,y });
	}

}
int main() {
	cin.sync_with_stdio(false);
	cin >> n;
	for (int i = 0; i < n; i++) {
		cin >> a[i];
	}
	dfs(0, 0, 0, 0);
	cout << P.size() << endl;
	return 0;
}

发布了21 篇原创文章 · 获赞 5 · 访问量 783

猜你喜欢

转载自blog.csdn.net/weixin_44578615/article/details/104909815