SDU csp第一次模拟

A:咕咕东的奇遇

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

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

output:
输出最少要转的次数。

样例:
输入:

zeus

输出:

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

思路:
起始位置是a,当前位置和下一个字符是没有干扰的,也就说说从当前字符转到下一个位置只有两种情况,顺时针转或者逆时针转。求解的方案也就变成了,判断顺时针和逆时针哪个步数更少。
将字符转换成26进制的数字

,也就是0~25。这里就涉及到一个怎么使0-25=1 的问题。
观察发现,假如在26进制下,有a,b 两个数,(a>b), 那么
a-b=a-b
b-a=26-(a-b)
;
这样首先比较然后计算就可以得到结果。

总结:
这次比较遗憾,因为没有过的原因是换行 用scanf("%c",&a[i]) 单个输入字符用的时候将换行符输入成了10 ,测试下得到的解决方案是,用gets输入字符串,或者用scanf("%s",a) 输入字符串,然后while(a[i]) 可以解决这个问题。
代码:

#include<stdio.h>
#include<iostream>
using namespace std;
char a[10001];
int count=0;
void fun(char c,char p) //目标字符c ,当前位置p
{
	int m,n,b,t,pp;
	b=(c-97)%26;
	t=(p-97)%26;
	if(b<t){
		pp=b;
		b=t;
		t=pp;
	}
	m=b-t;
	n=26-m;
	if(m<n){	
		count+=m;
	}
	else {
		count+=n;
	}
	return;
} 
int main()
{
	//scanf("%s",a);
	gets(a);
	char pp='a';
	int i=0;
	while(a[i])  //吃了一个回车 
	{
		 fun(a[i],pp);
		 pp=a[i];
		 i++;
	}
	printf("%d",count);
	return 0;
 } 

B:咕咕东想吃饭

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

input:
输入两行,第一行输入一个正整数n(1<=n<=100000)(1<=n<=100000),表示考试周的天数。

第二行有n个数,第i个数ai (0<=a i <=10000)表示第i天咕咕东要买的生煎的数量。
output:
如果可以满足咕咕东奇怪的要求,输出"YES",如果不能满足,输出“NO”。(输出不带引号)

样例:
输入:

4
1 2 1 2

输出:

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

思路:
题目中说清楚的是不用考虑钱的多少,同时根据数据规模来看,不需要计算处每一种方案,而且 根据这个券的情况,可以将所有数都考虑为奇数或者是偶数。
当前的偶数的话,直接两个生煎一对,必然满足需求;
如果是奇数的话,(这里测试的时候想的方案繁琐一点,将两种方案都说明)
(1-测试时)如果是奇数的话考虑下一个如果也是奇数,正好今天买的券明天用,就都是偶数可以解决,i+2;如果下一个是偶数下一个应该买的包子数量减一,(如果下一天的包子小于0 说明券浪费,结束,无法实现)i+1;
(2-简化版) 奇数的话,直接将下一天要买的生煎数减一,如果<0 退出,无法实现,i+1;
然后到最后一天的话,就看要买的生煎是奇数还是偶数,如果偶数可以实现。如果奇数,不能实现。
总结:
我觉得这个题最重要的是简化的思想,如果只是输出yes\no的话就不必仔细考虑方案,这样问题的难度也大大下降。
代码:

#include<stdio.h>
using namespace std;
int main()
{
	int n;
	int *a=new int [n];
	scanf("%d",&n); 
	for(int i=0;i<n;i++)
	{
		scanf("%d",&a[i]);
	}
	for(int i=0;i<n;)
	{
		if(i<n-1)   //n-2 
		{
			if(a[i]%2==1&&a[i+1]%2==1){
				i=i+2;
				if(i>n-1){
					printf("YES");
					return 0;
				}
			}
			else if(a[i]%2==0){
				i=i+1;
			}
			else if(a[i]%2==1&&a[i+1]%2==0){
				a[i+1]=a[i+1]-1;
				if(a[i+1]<0){
					printf("NO");
					return 0;
				}
				i=i+1;
			} 
		}
		else if(i==n-1){
			if(a[i]%2==0){
				printf("YES");
			}
			 else{
			 	printf("NO");
			 } 
			 return 0;
		}
	}
	return 0;
}

C:可怕的宇宙射线

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

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

样例:
输入:

4
4 2 2 3

输出:

39

样例图示:
在这里插入图片描述
思路:
因为最后要求求得就是覆盖点的个数,那么首先想到的就是利用set 的去重功能,将遍历到的点加入到set 里。
如果直接用BFS 和DFS 的话,时间复杂度必然太高。考虑到每次分裂的两个方向和原方向都是对称的,并且有这个样例的图可以看出来,假如向同一个方向遍历到最后一个点,可以通过对称得到所有点的情况。并且由于是45°分裂,对称的直线只有四种情况:y=x; y=-x; y=0; x=0;,根据堆成方程可以推出未知点。因此这个题采取DFS 和对称的思路解答。

接下来记录其中的一些注意事项:
(1)方向问题:因为这里最后的操作是要对称,因此每次都选取当前方向的右边(或左边)会方便很多,记录方向的二维数组也应该按顺序写,这样递归的时候记录下一次的方向更加方便。

int dx[]={0,1,1,1,0,-1,-1,-1};   //依次为 向上、右上、右、右下、下、左下、左、左上、 
int dy[]={1,1,0,-1,-1,-1,0,1};
dfs(index+1,(dir+1)%8,sx+a[index]*dx[dir],sy+a[index]*dy[dir]);   

(2)在dfs 最后 怎么加入点
首先对于对称的话,当前set 里的所有点及其对称点都要加入到set;
其次因为每次前进的步数是不一样的,“途中”的点也要加入到set 里。

再记录一个失败的例子:
因为每层所需要走的步数不一样,如果采用BFS 的话,需要将BFS 分层,这里提供一种分层BFS 的写法;
(3)下一次dfs 的写法:
每次都需要记录当前需要走多少步,方向是什么,起点是什么

dfs(index+1,(dir+1)%8,sx+a[index]*dx[dir],sy+a[index]*dy[dir]);

每次都需要记录当前需要走多少步,方向是什么,起点是什么

(4)再记录一个失败的例子:
因为每层所需要走的步数不一样,如果采用BFS 的话,需要将BFS 分层,这里提供一种分层BFS 的写法;

q.push(now);
q.push(p);   //p 为特殊标志位
while(!q.empty()){
	now=q.front();
	if(now==p){    //判断是否层结束层的结束 
		if(j==n-1)	break;
		j++;
		next1.step=a[j+1];
		next2.step=a[j+1];
		q.pop();   //将当前标志位pop
		q.push(p);//记录下一层的标志位
		continue ;

总结:

使用dfs 的一般框架:

void dfs(   )
{
		if(){  { //判断是否达到终点  
		dfs()   //没有达到的话继续递归
		visit();  //对当前内容进行操作
}

bfs 的一般框架

void bfs()
{
	while(!=q.empty)
	{
		now=q.front();q.pop();
	     if()  判断是否达到终点
	        //从当前now ,获取符合条件的next 加入q;
	        q.insert(next);
	    }
}

记录方向问题:如果是二维平面的话,可以开静态数组表示

代码:

#include<stdio.h>
#include<set>
#include<algorithm>
using namespace std;
struct point{
	int x;
	int y;
	bool operator < (const point &e)const{
		if(x!=e.x)return x<e.x;
 		else	return y<e.y;
	}	
};

int a[32];
int n;
int dx[]={0,1,1,1,0,-1,-1,-1};   //依次为 向上、右上、右、右下、下、左下、左、左上、 
int dy[]={1,1,0,-1,-1,-1,0,1};

set<point> s;
void dfs(int index,int dir,int sx,int sy)
{
	if(index>=n) return ;
	
	dfs(index+1,(dir+1)%8,sx+a[index]*dx[dir],sy+a[index]*dy[dir]);   //一直递归到最后一个点 
	
	set<point>::iterator it;
	for(it=s.begin();it!=s.end();it++)
	{
		point next;
		if(dir==0||dir==4){    //上下方向 
			next.x=2*sx - it->x;
			next.y=it->y; 
			s.insert(next);
		}
		if(dir==1||dir==5){    //y = x 
			next.x=it->y +sx-sy;
			next.y=it->x +sy-sx;
			s.insert(next);
		}
		if(dir==2||dir==6){   //水平方向 
			next.x=it->x;
			next.y=2*sy- it->y;
			s.insert(next);
		}
		if(dir==3||dir==7){    //y = -x 
			next.x=sx+sy- it->y;
			next.y=sx+sy- it->x;
			s.insert(next);
		}
	}
	
	for(int i=0;i<a[index];i++){
		point next;
		next.x=sx+dx[dir];
		next.y=sy+dy[dir];
		sx=next.x;
		sy=next.y;
		s.insert(next);
	} 
}
int main()
{
	scanf("%d",&n);
	for(int i=0;i<n;i++)
		scanf("%d",&a[i]);
	dfs(0,0,0,0);
	printf("%d",s.size());
 } 
发布了10 篇原创文章 · 获赞 0 · 访问量 170

猜你喜欢

转载自blog.csdn.net/weixin_45736432/article/details/104920273
今日推荐