程序设计思维CSP-M1赛后整理

咕咕东的奇遇

咕咕东是个贪玩的孩子,有一天,他从上古遗迹中得到了一个神奇的圆环。这个圆环由字母表组成首尾相接的环,环上有一个指针,最初指向字母a。咕咕东每次可以顺时针或者逆时针旋转一格。例如,a顺时针旋转到z,逆时针旋转到b。咕咕东手里有一个字符串,但是他太笨了,所以他来请求你的帮助,问最少需要转多少次。
在这里插入图片描述
Input
输入只有一行,是一个字符串。
Output
输出最少要转的次数。
Sample input
zeus
Sample output
18
解题思路
签到题,对于每次找字幕判断顺时针转比较近还是逆时针转比较近,累加即可

#include<iostream>
#include<cmath>
#include<algorithm>
#include <cstring>
#include <string>
#include <cstdio>
using namespace std;
#define ll long long
string s;
ll ans = 0;
int cal(char x,char y)
{
    int numx = x - 'a';
    int numy = y - 'a';
    if(numx > numy)
    {
        int t = numx;
        numx = numy;
        numy = t;
    }
    if((numy-numx) >= 0 && (numy-numx) < (numx+26-numy))
    {
        return (numy-numx);
    }
    else return (numx+26-numy);
}
int main()
{
    cin>>s;
    char c = 'a';
    for(int i=0;i<s.length();i++)
    {
        ans += cal(c,s[i]);
        c = s[i];
    }
    cout<<ans;
    return 0;
}

咕咕东想吃饭

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

Input
输入两行,第一行输入一个正整数n(1<=n<=100000),表示考试周的天数。
第二行有n个数,第i个数ai(0<=ai<=10000)表示第i天咕咕东要买的生煎的数量。
Output
如果可以满足咕咕东奇怪的要求,输出"YES",如果不能满足,输出“NO”。(输出不带引号)
Sample input1
4
1 2 1 2
Sample output1
YES
Sample input2
3
1 0 1
Sample output2
NO
解题思路
当时在做这道题的时候思路跑偏了,看了n的范围以为一定得遍历一下一天一天地记录答案,后来发现这样反而很麻烦,n * ai的枚举显然无法接受,后来想到分ai为奇数偶数的情况考虑就可以了,但是由于在错误的思路上跑了太久最后也没改回来,这题没AC实在是不应该。
解法是一个并不复杂的贪心,今天要买偶数个就买,要买奇数个且手里没券就让券个数加一,否则用一个券。到了要买0个的某一天或者到了最后手里还有废券则是不合法的

#include<iostream>
#include<cmath>
#include<algorithm>
#include <cstring>
#include <string>
#include <cstdio>
#include <queue>
using namespace std;
#define ll long long
int n,x,tickets;
bool flag;
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>x;
        if(x%2)
        {
            if(tickets == 0)
            {
                tickets++;
            }
            else
            {
                tickets--;
            }
        }
        else
        {
            if(x==0)
            {
                if(tickets)
                {
                    flag = true;
                    break;
                }
            }
            
        }
    }
    if(tickets%2||flag)
    {
        cout<<"NO";
    }
    else
    {
        cout<<"YES";
    }
    return 0;
}

可怕的宇宙射线

宇宙射线深入人心
众所周知,瑞神已经达到了CS本科生的天花板,但殊不知天外有天,人外有苟。在浩瀚的宇宙中,存在着一种叫做苟狗的生物,这种生物天生就能达到人类研究生的知识水平,并且天生擅长CSP,甚至有全国第一的水平!但最可怕的是,它可以发出宇宙射线!宇宙射线可以摧毁人的智商,进行降智打击!
宇宙射线会在无限的二维平面上传播(可以看做一个二维网格图),初始方向默认向上。宇宙射线会在发射出一段距离后分裂,向该方向的左右45°方向分裂出两条宇宙射线,同时威力不变!宇宙射线会分裂n次,每次分裂后会在分裂方向前进ai个单位长度。
现在瑞神要带着他的小弟们挑战苟狗,但是瑞神不想让自己的智商降到普通本科生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
解题思路
比赛时看出来要出正解就要结合数学推导,但是这个过程没有看明白,自己随便蒙了个式子上去也没得分。赛后逛了很久别人的博客,学到了解决办法。
这个图是一个不断往外对称的过程,由于越往后分裂,图的分叉越多,所以我们从最后一次对称回溯往前算,将所有的点加入set,因为set内部元素有序且自动去重,记录答案很方便,最后输出size就是答案。

#include<iostream>
#include<cmath>
#include<algorithm>
#include <cstring>
#include <string>
#include <cstdio>
#include <queue>
#include <set>
using namespace std;
#define ll long long
struct node
{
    int x,y;
    bool operator < (const node& b)const
    {
        return x != b.x ? x < b.x : y < b.y;
    }
};
set<node>mmap;
int v[51],n;
int qx[] = {0,1,1,1,0,-1,-1,-1};
int qy[] = {1,1,0,-1,-1,-1,0,1};
void dfs(node p,int cnt,int direct)
{
    if(cnt > n)return;
    dfs({p.x + qx[direct] * v[cnt], p.y + qy[direct] * v[cnt]},cnt + 1,(direct + 1)%8);//往后分裂
    set<node>temp;//记录本次分裂往前延申到的点
    for(auto& i : mmap)
    {
        if(direct == 0 || direct == 4)
        {
            temp.insert({p.x * 2 - i.x,i.y});
        }
        else if(direct == 2 || direct == 6)
        {
            temp.insert({i.x,p.y * 2 - i.y});
        }
        else if(direct == 1 || direct == 5)
        {
            temp.insert({i.y + p.x - p.y,i.x + p.y - p.x});
        }
        else if(direct == 3 || direct == 7)
        {
            temp.insert({p.x + p.y - i.y,p.x + p.y - i.x});
        }
    }
    mmap.insert(temp.begin(),temp.end());
    for(int i = 1; i<= v[cnt]; i++)
    {
        mmap.insert({p.x + qx[direct] * i,p.y + qy[direct] * i});
    }
}
int main()
{
    cin>>n;
    for(int i = 1;i <= n;i++)
    {
        cin>>v[i];
    }
    dfs({0,0},1,0);
    cout<<mmap.size();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值