week4 CSP模拟

A-咕咕东的奇遇

题目描述

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

输入格式

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

输出格式

输出最少要转的次数。

样例输入

zeus

样例输出

18

 

思路

题目并不难,只需要每次找到最短路径然后更新位置即可。

找到最短路径的过程也很简单,因为一共26个字母,每次最短路径必定不超过13,如果超过13则用26减去该路径(即反方向转动)。

可以直接将字符转成int型,转出来的结果是字母的ASCII码。

PS

我写的时候思路有点清奇,我申请了一个int型的数组,一个一个输入的字符分别转化成int型。其实完全可以直接输入字符串然后再进行转化。

 

代码

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int c[10010];
int total = 0;

int main()
{
	memset(c, 0, sizeof c);
	char ch;
	int i = 0;
	while ((ch = cin.get()) != EOF)
	{
		if (ch == '\n') break;
		c[i] = (int)ch;
		i++;
		
	}

	int begin = 97;
	i = 0;
	while (c[i] != 0)
	{
		int b = c[i] - begin;
		if (b < 0) b = -b;
		if (b > 13) total += (26 - b);
		else total += b;
		begin = c[i];
		i++;
	}

	std::cout << total << endl;
	system("Pause");
}

 

 

 

 

B-咕咕东想吃饭

题目描述

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

 

思路

这道题可以用递归进行求解,但我更喜欢不递归的写法。

首先分析这道题题目,不管是方案1还是方案2,因为有券必须用完的限制,所以若可以满足条件,生煎数目必须是偶数个。

其次,如果从前向后进行循环的话,并不能很好地选择方案,因为无法预知后面的天数会发生什么情况(虽然可以暴力求解,把每个情况尝试一遍)。因此循环是从最后一天开始的。

经过分析,选择奇数次方案2与选择一次结果是一样的,因为超过一次的奇数次,前后两天都可被方案1代替。

那么从后向前循环,改天生煎数为奇数,选择一次方案2并减少前面一天的一个生煎,否则选择方案1。直到第一天,如果剩余生煎数为奇数个或为负,则无法满足要求。

 

代码

#include <iostream>
using namespace std;
int total;
int sum;


int main()
{
	bool ok = true;
	cin>>total;
	int *a = new int[total];
	sum = 0;
	for(int i = 0; i < total; i++)
	{
		cin>>a[i];
		sum += a[i];
	}
	
	if(sum%2 == 1) ok = false;//总数是奇数 
	
	for(int i = total - 1; i >= 0 ;i--) 
	{
		if(a[i]%2==1 && i==0)
		{
			ok = false;
			break;
		}
		else if(a[i] < 0)
		{
			ok = false;
			break;
		}
		else if(a[i]%2==1) a[i-1] -= 1;
	}
	
	if(ok) cout<<"YES"<<endl;
	else cout<<"NO"<<endl; 
	
}

 

 

 

 

 

C - 可怕的宇宙射线

 

样例输入

4
4 2 2 3

样例输出

39

 

思路

我第一反应就是,既然是一层一层扩散的那就用bfs吧。因为申请了一个二维数组来记录该点有没有到达,涉及到坐标问题,所以并没有使用while(!q.empty( ) ) 这样的循环。

使用偏移量dx,dy来标记了每一个方向,这里是按照顺时针方向进行标记的(当然也可以逆时针方向标记,但是一定要标记成一个圈,不然后续选择方向就会很麻烦)。

声明一个point记录每个点的坐标与方向,每次将point加入队列。

过程

首先将起始点加入队列,根据方向进行扩散,沿途标记每一个经过的点,并将当前的终点加入队列。

剪枝

稍微思考一下,我们就可以知道,因为每次是向两个方向进行扩散的,所以这是一个指数级的问题,那么我们就要考虑剪枝。

剪枝方法是,将同一层的再一次到达同一个点并且同方向的射线剪枝。为什么需要同一层呢,因为如果不在同一层,下一次扩散的距离很可能不同,结果自然就会出错。因此声明了cut数组进行剪枝,每一次扩散之前先将cut数组清空。

PS

我考虑过对称性的问题,因为第一次一定是向上的,不难理解这个图是左右对称的。利用显然可以节省空间,但是会存在放射到中轴线由于数组空间不足而需要反射的情况,实现起来较为复杂。

 

代码

#include <iostream>
#include <cstring>
#include <queue>
#include <cstring>
using namespace std;
int dx[8] = { 0, -1, -1, -1, 0, 1, 1, 1 };
int dy[8] = { 1, 1, 0, -1, -1, -1, 0, 1 };
int total;
bool is[500][500];
bool cut[500][500][8];

struct point
{
	pair<int, int> p;
	int direct;
	point(pair<int, int> &p1, int d)
	{
		p = p1;
		direct = d;
	}
};



int main()
{
	total = 0;
	memset(is, 0, sizeof is);
	int n;//分裂次数 
	cin >> n;
	int *a = new int[n];

	for (int i = 0; i < n; i++)
		cin >> a[i];

	pair<int, int> san1(250, 250 + a[0] - 1);
	for (int i = 0; i < a[0]; i++)
		is[250][250 + i] = true;

	struct point po(san1, 0);
	queue<struct point> q;
	q.push(po);
	int go = 1;

	for (int i = 1; i < n; i++)
	{
		//cout << "go" << go << endl;
		int es = go;
		go = 0;
		memset(cut, 0, sizeof cut);
		//cout << "es" << es << endl;
		for (int j = 0; j < es; j++)
		{
			//cout<<i<<" "<<j<<endl;
			struct point p1 = q.front();
			int x = p1.p.first;
			int y = p1.p.second;
			int pot = p1.direct;
			pair<int, int> pp3 = p1.p;
			//cout << "pot:" << pot << endl;
			if (pot == 0)
			{
				//cout<< cut[x][y][0] <<endl;
				if (cut[x][y][0] == 0)
				{
					pair<int, int> pp3;
					cut[x][y][0] = 1;
					for (int k = 1; k <= a[i]; k++)
						is[x + k * dx[1]][y + k * dy[1]] = true;

					pp3.first = x + a[i] * dx[1];
					pp3.second = y + a[i] * dy[1];
					//cout << pp3.first << " " << pp3.second << endl;

					struct point p3(pp3, 1);
					q.push(p3);
					//cout << "ye!!" << endl;
					go++;

					pair<int, int> pp4;
					pp4.first = x + a[i] * dx[7];
					pp4.second = y + a[i] * dy[7];
					//cout << pp4.first << " " << pp4.second << endl;
					for (int k = 1; k <= a[i]; k++)
						is[x + k * dx[7]][y + k * dy[7]] = true;
					struct point p4(pp4, 7);
					q.push(p4);
					go++;
				}
				q.pop();
			}

			else if (pot == 7)
			{
				if (cut[x][y][7] == 0)
				{
					pair<int, int> pp3;
					cut[x][y][7] = 1;
					for (int k = 1; k <= a[i]; k++)
						is[x + k * dx[0]][y + k * dy[0]] = true;

					pp3.first = x + a[i] * dx[0];
					pp3.second = y + a[i] * dy[0];
					//cout << pp3.first << " " << pp3.second << endl;

					struct point p3(pp3, 0);
					q.push(p3);
					go++;

					pair<int, int> pp4;
					pp4.first = x + a[i] * dx[6];
					pp4.second = y + a[i] * dy[6];
					//cout << pp4.first << " " << pp4.second << endl;
					for (int k = 1; k <= a[i]; k++)
						is[x + k * dx[6]][y + k * dy[6]] = true;
					struct point p4(pp4, 6);
					q.push(p4);
					go++;
				}
				q.pop();
			}
			else
			{
				if (cut[x][y][pot] == 0)
				{
					pair<int, int> pp3;
					cut[x][y][pot] = 1;
					for (int k = 1; k <= a[i]; k++)
						is[x + k * dx[pot + 1]][y + k * dy[pot + 1]] = true;

					pp3.first = x + a[i] * dx[pot + 1];
					pp3.second = y + a[i] * dy[pot + 1];
					//cout << pp3.first << " " << pp3.second << endl;

					struct point p3(pp3, pot + 1);
					q.push(p3);
					go++;

					pair<int, int> pp4;
					pp4.first = x + a[i] * dx[pot - 1];
					pp4.second = y + a[i] * dy[pot - 1];
					//cout << pp4.first << " " << pp4.second << endl;
					for (int k = 1; k <= a[i]; k++)
						is[x + k * dx[pot - 1]][y + k * dy[pot - 1]] = true;
					struct point p4(pp4, pot - 1);
					q.push(p4);
					go++;
				}
				q.pop();
			}
		}
	}

	for (int i = 0; i < 500; i++)
		for (int j = 0; j < 500; j++)
			if (is[i][j] == 1)
			{
				total++;
				//cout << "(" << i << "," << j << ")";
			}

	cout << total << endl;
	//std::system("pause");
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值