《程序设计思维与实践》week4 CSP—M1

A — 咕咕东的奇遇

题目描述

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

Input

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

Output

输出最少要转的次数。

Sample Input

zeus

Sample Output

18

解题思路

旋转只存在两个方向,要么顺时针,要么逆时针,所以要求旋转的次数,实际上就转换成两个字母之间最短的距离;

distance( ) 函数:
获取顺、逆时针两种情况下的距离更短的那种。逆时针,大的字符减去小的那个;顺时针,两个字符到字符‘a’的最短的距离之和;

解题代码

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

int distance(char a, char b) {
    char max, min;
    if (a >= b) {max = a; min = b;}
    else {max = b; min = a;}
    int ds = (min - 97) + (26 + 97 - max);
    int dn = max - min;
    if (ds >= dn) {return dn;}
    else
        return ds;
}

int main()
{
    string s;
    cin>>s;
    int sum = 0;
    sum = sum + distance('a', s[0]);
    for (int n = 1; n < s.size(); n++) {
        sum = sum + distance(s[n-1], s[n]);
    }
    cout<<sum;
}

B — 咕咕东想吃饭

题目描述

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

Input

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

Output

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

Sample Input

4
1 2 1 2

Sample Output

YES

解题思路

用一个vector数组存储每天要买的生煎的数量,从第一天开始判断,对于数据要分成两种情况考虑:
(1)为偶数,那么采取一次性买两个的方案;
(2)为奇数,在满足当天的需求之后,还要考虑购买券的问题,将后一天的要买的数量减一;
对于最终需要输出的情况,用一个常量complete表示结果,
满足条件的时候值为1;
不满足条件的情况,如,要购买的生煎数量为负数其值变为-1;特别地,对于最后一天,不满足条件的情况除了为负数之外,为奇数也是不可以的;
结果输出:
compelete = 1,输出 YES;
compelete = -1,输出NO;

解题代码

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

int n;
vector<int> a;
int compelete = 1;

int main()
{
    cin>>n;
    int number;
    for (int m = 0; m < n; m++) {
        cin>>number;
        a.push_back(number);
    }
    for (int m = 0; m < n - 1; m++) {
        if (a[m] < 0) {
            compelete = -1;
            break;
        }
        else {
            if (a[m] % 2 == 0)
                continue;
            else
                a[m+1]--;
        }
    }
    if (a[n-1] % 2 != 0 || a[n-1] < 0) compelete = -1;
    if (compelete == -1)
        cout<<"NO";
    else
        cout<<"YES";
    
    return 0;
}

C — 可怕的宇宙射线

题目描述

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

Input

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

Output

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

Sample Input

4
4 2 2 3

Sample Output

39

解题思路

由简单的几何知识可以知道,分裂之后的方向总共8种可能,所以用dx[ ],dy[ ]储存往8个方向的坐标变化;
每一次的分裂之后,产生的都是一些对称的点,所以要从最后的一次分裂开始求对称点;
从最后一次开始分裂之后的边开始,一直以前一条边为对称轴求其对称的点,求完对称点之后,再将这一级分裂的路径上的点加进存储的数组中。
用一个链表存储每一层分裂的所有点,用一个set数组,根据其能够自动去重的特性,用来存储所有的“降维”之后的点。
对于所有的分裂,对称的情况共四种,关于X轴、Y轴、直线y = x + k、直线y = -x + k对称,每次从set数组的首元素开始,一直到最后一个元素,每一个都按照相应的规则去获得对称点,再将得到的对称点存储进set数组中。
最后所有“被降维”的点的个数就是set数组的size大小。

解题代码

#include <iostream>
#include <string>
#include <vector>
#include <set>
#include <list>
#include <utility>
#include <cmath>
using namespace std;

int dx[] = {0, 1, 1, 1, 0, -1, -1, -1};
int dy[] = {1, 1, 0, -1, -1, -1, 0, 1};

struct point {
    int x;
    int y;
    
    point() {x = 0; y = 0;}
    point operator = (point g) {
        x = g.x;
        y = g.y;
        return *this;
    }
    bool operator < (point g) const {
        return  (x < g.x) ? true : y < g.y;
    }
};

struct PX {
    bool operator() (const point& w, const point& e) const{
        if (w.x == e.x) {
            return w.y < e.y;
        }
        return w.x < e.x;
    }

};

int n;
vector<int> a;
set<point, PX> d;
list<point> dd;

void fenlie(int q, int w, int e, point p){ //q表示当前的分裂次数;w表示总共需要分裂的次数;e表示初始的方向;a[q]表示长度;p表示出发点的坐标
    for (int b = 0; b < a[q]; b++) {p.x += dx[e]; p.y += dy[e]; dd.push_front(p);}
    //最后一次分裂的路径
    if (q == w - 1) {
        for (int b = 0; b < a[q]; b++) {
            point pp = dd.front();
            d.insert(pp);
            dd.pop_front();
        }
        return;
    }
    fenlie(q + 1, w, (e + 1) % 8, p);
    
    point pp;
    int c = p.y - p.x;
    int cc = p.y + p.x;
    //根据之前的边求他们的对称点
    if (e == 0 || e == 4) {
        for (set<point, PX>::iterator it = d.begin(); it != d.end(); it++) {
            pp.x = 2 * p.x - it->x; pp.y = it->y; d.insert(pp);
        }
    }
    if (e == 1 || e == 5) {
        for (set<point, PX>::iterator it = d.begin(); it != d.end(); it++) {
            pp.x = it->y - c; pp.y = it->x + c; d.insert(pp);
        }
    }
    if (e == 2 || e == 6) {
        for (set<point, PX>::iterator it = d.begin(); it != d.end(); it++) {
            pp.x = it->x; pp.y = 2 * p.y - it->y; d.insert(pp);
        }
    }
    if (e == 3 || e == 7) {
        for (set<point, PX>::iterator it = d.begin(); it != d.end(); it++) {
            pp.x = cc - it->y; pp.y = cc - it->x; d.insert(pp);
        }
    }
    //将这一层的分裂的路径的点存储进去
    for (int b = 0; b < a[q]; b++) {
        point pp = dd.front();
        d.insert(pp);
        dd.pop_front();
    }
    return;
}

int main()
{
    cin>>n;
    int length;
    for (int m = 0; m < n; m++) {
        cin>>length;
        a.push_back(length);
    }
    
    point z;
    fenlie(0, n, 0, z);
    cout<<d.size()<<endl;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值