程序设计 CSP-M1 补题

一、咕咕东的奇遇

1.题目

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

输出格式输出最少要转的次数。

样例输入

zeus

样例输出

18

2.解题思路

minLen为计算两个字母a、b的距离。对于除a=b、(a+13)%26=b之外的其他情况,让b成为ascll码较大的那个字母,若b-a为正,则返回b-a,否则返回26-(b-a)。

3.c++代码

#include<iostream>
#include<string>
#include<cstring>
using namespace std;

int minLen(char a,char b)//从a到b最短路径 
{
	a=a-97;
	b=b-97;
	if(a==b)return 0;
	if((a+13)%26==b)return 13;
	if(a>b)//让a<b 
	{
		int t;
		t=a;
		a=b;
		b=t;
	}
	if(b>a)
	{
		if(b-a<13)return b-a;
		else return 26-(b-a);
	}
	
} 
int main()
{
	char ch[10001];
	char c;
	cin>>ch;
	int ans=minLen('a',ch[0]);
	for(int i=0;i<strlen(ch)-1;i++)//寻找strlen-1次 
		ans+=minLen(ch[i],ch[i+1]);
	cout<<ans<<endl;
	return 0; 
 } 

二、B - 咕咕东想吃饭

1.题目

咕咕东考试周开始了,考试周一共有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

2.解题思路

第一种购买方式是一次买偶数个生煎,第二种是第一天买奇数个生煎,同时第二天要买的生煎数减一。也就是若有一天的生煎数为奇数,则必须要选择第二种购买方式。

如此按天数循环,若该天需要买的生煎是偶数,可以选择第一种购买方式全部买完,直接进入第二天。若为奇数,则选择第二种方式购买一个生煎,剩下的偶数个生煎用第一种方式购买,同时第二天要买的生煎数减一。循环到最后一天,若生煎数为偶数,则成功,否则失败(因为选择第二种方式购买后手中有券存留)。

注意,该数据点每天的生煎数最大范围为10w,故采用while而非用数组存放每天的生煎数。

3.c++代码

#include<iostream>
#include<string>
#include<cstring>
using namespace std;


int main()
{
	int n,x,t=0;
	cin>>n;
	while(n--)
	{
		cin>>x;
		x-=t;
		if(x%2==0&&n==0)
		{
			cout<<"YES"<<endl;
			break; 
		}
		else if(x%2==1&&n==0)
		{
			cout<<"NO"<<endl;
			break; 
		}
		if(x%2==0)
			t=0; 
		else if(x%2==1)
			t=1;
		
	}
	return 0;
}

三、C - 可怕的宇宙射线

1.题目

众所周知,瑞神已经达到了CS本科生的天花板,但殊不知天外有天,人外有苟。在浩瀚的宇宙中,存在着-种叫做苟狗的生物, 这种生物天生就能达到人类研究生的知识水平,并且天生擅长CSP,甚至有全国第一的水平!但最可怕的是,它可以发出宇宙射线!宇宙射线可以摧毁人的智商,进行降智打击!

宇宙射线会在无限的二维平面上传播(可以看做一个二维网格图),初始方向默认向上。宇宙射线会在发射出一段距离后分裂,向该方向的左右45°方向分裂出两条宇宙射线,同时威力不变!宇宙射线会分裂n次,每次分裂后会在分裂方向前进a个单位长度。

现在瑞神要带着他的小弟们挑战苟狗,但是瑞神不想让自己的智商降到普通本科生zjm那么菜的水平,所以瑞神来请求你帮他计算出共有多少个位置会被"降智打击”。
在这里插入图片描述

样例输入

4
4 2 3 2

样例输出

39

2.解题思路

以第一次分裂的第一个点表示为二维坐标(0,1),结构体存储每个格的坐标。每次分裂都有两个方向,故可用递归实现。分裂后的走向有八种,可用0~7表示,并用set存储走过的方格,因为set可以自动去重复,所以可以不用考虑射线走到同一个格的情况,但是要重载<函数。递归时,将射线走过的方格放进set,并按照射线的方向分类讨论分裂后的方向。直到分裂次数到达N就返回。但是这样写完只能40分,剩下的数据点会超时。

又注意到在第六七次的分裂后,会有大量的在同一分裂层次、同一方向并处于同一点的情况,这时就不必再递归,因为后续分裂情形已发生过。这样就不会超时了,可AC。

3.c++代码

#include<iostream>
#include<map> 
#include<set>
using namespace std;

struct coordinate
{
	int x;
	int y;
	
	bool operator<(const coordinate& c)const
	{
		if(x!=c.x)return x<c.x;
		else return y<c.y;
	}
};

int splitLen[40];//每次分裂后还要前进的长度 
int count=0;//答案 
int n;//共分裂几次 
set<coordinate> thisS;
bool bett[10][50][500][500]={false};

void s(coordinate thisCo,int dir,int num)
{
	//coordinate co;
	if(num==n)//截止条件
		return;
	if(bett[dir][num][thisCo.x][thisCo.y])
	//判断在当下分裂次数、分裂方向、分裂坐标的情况是否已存在,若存在就无需进行分裂
		return;
	bett[dir][num][thisCo.x][thisCo.y]=true;
	if(dir==0)//向上 
	{
		int len=splitLen[num];
		while(len--)
		{
			thisCo.y+=1;
			thisS.insert(thisCo);
		}
		num++;
		s(thisCo,1,num);
		s(thisCo,7,num);
	} 
	if(dir==1)
	{
		int len=splitLen[num];
		while(len--)
		{
			thisCo.x+=1;thisCo.y+=1;
			thisS.insert(thisCo);
		}
		num++;
		s(thisCo,0,num);
		s(thisCo,2,num);
	}
	if(dir==2)
	{
		int len=splitLen[num];
		while(len--)
		{
			thisCo.x+=1;
			thisS.insert(thisCo);
		}
		num++;
		s(thisCo,1,num);
		s(thisCo,3,num);
	}
	if(dir==3)
	{
		int len=splitLen[num];
		while(len--)
		{
			thisCo.x+=1;thisCo.y-=1;
			thisS.insert(thisCo);
		}
		num++;
		s(thisCo,2,num);
		s(thisCo,4,num);
	}
	if(dir==4)
	{
		int len=splitLen[num];
		while(len--)
		{
			thisCo.y-=1;
			thisS.insert(thisCo);
		}
		num++;
		s(thisCo,3,num);
		s(thisCo,5,num);
	}
	if(dir==5)
	{
		int len=splitLen[num];
		while(len--)
		{
			thisCo.x-=1;thisCo.y-=1;
			thisS.insert(thisCo);
		}
		num++;
		s(thisCo,4,num);
		s(thisCo,6,num);
	}
	if(dir==6)
	{
		int len=splitLen[num];
		while(len--)
		{
			thisCo.x-=1;
			thisS.insert(thisCo);
		}
		num++;
		s(thisCo,5,num);
		s(thisCo,7,num);
	}
	if(dir==7)
	{
		int len=splitLen[num];
		while(len--)
		{
			thisCo.x-=1;thisCo.y+=1;
			thisS.insert(thisCo);
		}
		num++;
		
		s(thisCo,6,num);
		s(thisCo,0,num);
	}
} 
 
int main()
{
	int dir=0;//0~7表示向上顺时针方向的编号 
	cin>>n;
	for(int i=0;i<n;i++)
		cin>>splitLen[i];
	coordinate thisCo;thisCo.x=0;thisCo.y=0;
	s(thisCo,0,0);
	cout<<thisS.size()<<endl;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值