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());
}