程序设计思维 CSP-M1

A-咕咕东的奇遇

题目

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

示意图

在这里插入图片描述

思路

做当前点过圆心的直径,将环分为两半,若在左半部分,则顺时针转;若在右半部分,则逆时针转。即考虑两个字符的ascii码差的绝对值,若小于13,则顺时针;若大于13,则逆时针;若等于13,则皆可。

代码

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

int main() {
    string s;
    cin>>s;
    int begin=0,sum=0;
    for(int i=0;i<s.length();i++){
        int tmp=abs(begin-(s[i]-97));
        if(tmp<13){
            sum+=tmp;
        }
        else{
            sum+=(26-tmp);
        }
        begin=s[i]-97;
    }
    cout<<sum;
    return 0;
}

B-咕咕东想吃饭

题目

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

思路

因为不允许结束时手中有券,故买生煎时尽可能不使用方案二,则手中的券数要么为1要么为0。考虑第i天,若第i天买的生煎为0:

  1. 手中有券,则券浪费,不可以。
  2. 手中无券,则可继续。

若第i天买的生煎为偶数:

  1. 手中有券,用掉一张券。买的生煎数变为奇数,使用一次方案二,剩余采用方案一,则仍有一张券。
  2. 手中无券,全部使用方案二,仍为无券。

若第i天买的生煎为奇数:

  1. 手中有券,用掉一张券。买的生煎数变为偶数,全部使用方案二,手中变为无券。
  2. 手中无券。使用一次方案一,剩余使用方案二,手中变为一张券。

代码

#include <iostream>
using namespace std;

int main() {
    int n,num,quan=0;
    cin>>n;
    for(int i=0;i<n;i++){
        cin>>num;
        if(num<0){
            cout<<"NO";
            return 0;
        }
        else if(num==0){
            if(quan!=0){
                cout<<"NO";
                return 0;
            }
        }
        else if(num%2==1){
            if(quan==1)
                quan=0;
            else
                quan=1;
        }
    }
    if(quan!=0)
        cout<<"NO";
    else
        cout<<"YES";
    return 0;
}

总结

一开始有一个测试点没过是因为考虑错了买0个生煎的情况,以为只要买0个生煎,券浪费但仍可以继续。

C-可怕的宇宙射线

题目

宇宙射线会在无限的二维平面上传播(可以看做一个二维网格图),初始方向默认向上。宇宙射线会在发射出一段距离后分裂,向该方向的左右45°方向分裂出两条宇宙射线,同时威力不变!宇宙射线会分裂 次,每次分裂后会在分裂方向前进 个单位长度。
现在瑞神要带着他的小弟们挑战苟狗,但是瑞神不想让自己的智商降到普通本科生 那么菜的水平,所以瑞神来请求你帮他计算出共有多少个位置会被"降智打击"。

思路

被射线覆盖的点最多有300*300,进行记忆化dfs,并进行可行性剪枝。使用四维数组记录每次分裂开始的点,使用二维数组记录射线覆盖的点。求分裂后的方向时,可以求模,或再定义两个方向数组用于记录分裂后的射线方向。

代码

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

int n,ans=0;
int dx[]={0,-1,-1,-1,0,1,1,1};
int dy[]={1,1,0,-1,-1,-1,0,1};
int length[35];
bool vis[310][310],flag[35][305][305][7];

void dfs(int s,int x,int y,int dir){
    if(s>n)//分裂次数超过n
        return;
    if(flag[s][x][y][dir])
        return;
    flag[s][x][y][dir]=true;
    for(int i=1;i<=length[s-1];i++){
        if(!vis[x+i*dx[dir]][y+i*dy[dir]]){
            vis[x+i*dx[dir]][y+i*dy[dir]]=true;
            ans++;
        }
    }
    dfs(s+1, x+length[s-1]*dx[dir], y+length[s-1]*dy[dir], (dir+1)%8);
    dfs(s+1, x+length[s-1]*dx[dir], y+length[s-1]*dy[dir], (dir+7)%8);
}

int main() {
    scanf("%d",&n);
    for(int i=0;i<n;i++){
        scanf("%d",&length[i]);
    }
    memset(vis,false,sizeof(vis));
    memset(flag, false, sizeof(flag));
    dfs(1, 155, 155, 0);
    printf("%d",ans);
    return 0;
}

总结

一开始没有想到对分裂后方向的一个比较好的处理,写起来很麻烦,后来想到取模就可。

发布了24 篇原创文章 · 获赞 2 · 访问量 435

猜你喜欢

转载自blog.csdn.net/weixin_43805228/article/details/104980015