Week4 CSP

A - 咕咕东的奇遇

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

Input

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

Output

输出最少要转的次数。

Example Input

zeus

Example Output

18

Notes

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

题意:
给出一个字符串,计算转出这个字符串最少需要转多少次。
分析:
26个字母围成一个圈,每次转动时,最多只能是13次,只需要求出顺时针或者逆时针转动时的最小值,每次转动结束后需要记住停在哪一个字母,遍历字符串即可。
代码如下:

#include<iostream>
#include<cstdio>
#include<string>
#include<string.h>
#include<algorithm>
using namespace std;

char x[10010];

int main()
{
	scanf("%s",x);
	int m=strlen(x);
	int now=0;
	int ans=0;
	int t,min,a,b;
	for(int i=0;i<m;i++)
	{
		t=x[i]-'a';
		a=abs(t-now);
		b=26-abs(t-now);
		if(a<=b)
			min=a;
		else
			min=b;
		ans+=min;
		now=t;
	}
	printf("%d",ans);
	return 0;
}

B - 咕咕东想吃饭

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

Input

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

Output

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

Example Input

4
1 2 1 2

Example Output

YES

Notes

数据点 n(上限) a[i]​(上限)
1,2 10 10
3,4,5 1000 10
6,7 10 10000
8,9,10 100000 10000

题意:
给出两钟消费方式,再给出一个数组,试问能不能用所给的消费方式组成所给的数组。
分析:
首先我们来分析一下这两种消费方式,第一种一天买两个,第二种相当于连续两天各买一个,应该首先选择第一种方式,因为其对于后续没有影响,当某一天为奇数时,只能选择第二种方式,但这种方式需要对下一天的数组进行操作,对每个a[i],每次操作相当于减去一个数,当a[i]中的任何一个(包括a[n])变为负数时,则输出“NO”,否则输出“YES”。
代码如下:

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

int main()
{
	int n;
	cin >> n;
	int* a = new int[n + 1];
	for (int i = 0; i < n; i++)
	{
		cin >> a[i];
	}
	a[n] = 0;
	for (int i = 0; i < n; i++)
	{
		if (a[i] < 0)
		{
			cout << "NO" << endl;
			return 0;
		}
		if (a[i] % 2 == 1)
		{
			a[i] = 0;
			a[i + 1]--;
		}
	}
	if (a[n] < 0)
	{
		cout << "NO" << endl;
	}
	else
	{
		cout << "YES" << endl;
	}
	return 0;
}

C-可怕的宇宙射线

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

Input

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

Output

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

Example Input

4
4 2 2 3

Example Output

39

Notes

数据点 n
10% <=10
40% <=20
100% <=30

题意:
一束会分裂的宇宙射线,每次分裂成两股,分裂n次后,计算出射线所覆盖的范围。
分析:
这道题做了好久,每次都是时间复杂度太高,后来发现还是没有弄清楚问题的本质,直接就开始胡乱dfs,后来听助教讲解过之后,才发现还是没有掌握做这一类题目的方法,没有理清楚思路就开始动手,后来不断拆东墙补西墙。
射线的最大活动范围是一个300*300的正方形,所以可以使用一个bool型的数组来对每一个点进行标记,射线一共有八个前进的方向,也可以用定值的数组来控制分裂的次序,同一个点在同一个方向,并且在同一次分裂时就可以进行剪枝,并且不会影响后续的分裂。防止重复dfs造成超时。还有一个细节问题,刚开始将原点定在了(0,0),没有考虑到这样会造成产生负值,后来定为(150,150)才解决问题。
代码如下:

#include<iostream>
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 };

//每个顶点的标记
bool point[300][300][30][8] = { false };
//空间中每个点的标记
bool label[300][300] = { false };

int a[30] = { 0 };
int n = 0;
int ans = 0;

void dfs(int x, int y, int m, int d) 
{
	if (m == n)
		return;
	if (!point[x][y][m][d]) 
	{//这个点没有分裂过,开始分裂
		point[x][y][m][d] = true;  
		for (int i = 1; i <= a[m]; i++) 
		{
			if (!label[x + dx[d] * i][y + dy[d] * i]) 
			{
				label[x + dx[d] * i][y + dy[d] * i] = true;
				ans++;
			}
		}
		dfs(x + dx[d] * a[m], y + dy[d] * a[m], m + 1, (d + 1) % 8);//继续右侧分裂
		dfs(x + dx[d] * a[m], y + dy[d] * a[m], m + 1, (d + 7) % 8);//继续左侧分裂 
	}
}


int main()
{
	cin >> n;
	for (int i = 0; i < n; i++)
		cin >> a[i];
	dfs(150, 150, 0, 0);
	cout << ans;
	return 0;
}
发布了14 篇原创文章 · 获赞 0 · 访问量 298

猜你喜欢

转载自blog.csdn.net/qq_43808194/article/details/104979543