第一次CSP模测

A-咕咕东的奇遇

题目描述

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

输入格式

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

输出格式

输出最少要转的次数。

样例输入

zeus

样例输出

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

题解

大致题意:通过转动圆环让指针按顺序指完字符串中所有字母,并使转动圆环的次数最少。

转动圆环只有两种方向选择:顺时针和逆时针,那么问题就在于每次如何选择转动方向。

通过多次举例画图、总结数学公式可以发现,无论是逆时针转还是顺时针转,最短的路径不会超过12,一旦超过了12那么用反方向转的结果一定更优。

所以将相邻字母对应的ASCII码相减取绝对值,如果<=12,那么该绝对值就是这次要转动的次数;如果>12,选择反方向转动,需要用26减去该绝对值(举几个例子画画图总结就有了)。

完整代码

#include <iostream>
#include <string.h>
#include <cstring>
#include <cmath>
using namespace std;
int main()
{
	string s;
	int n;
	cin>>s;
	n = s.size();
	int f = 1;
	int ans = 0;
	for(int i=0;i<n;i++)
	{
		int k = abs(s[i]-96-f);
		if(k<=12)	ans+=k;
		else if(k>=13)	ans+=(26-k);
		f = s[i]-96;
	}
	cout<<ans<<endl;
	return 0;
}

总结

这一题不难,重点是读懂题意,然后关注字母差值和转动次数之间的数学关系就行了,总结规律是关键。

B-咕咕东想吃饭

题目描述

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

输入格式

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

第二行有n个数,第i个数 ai(0<=ai<=10000,表示第i天咕咕东要买的生煎的数量。

输出格式

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

样例输入1

4
1 2 1 2

样例输出1

YES

样例输入2

3
1 0 1

样例输出2

NO

在这里插入图片描述题解

咕咕东买生煎一共只有两种购买方式:
①在某一天一次性买两个生煎。
②今天买一个生煎,同时为明天买一个生煎,店家会给一个券,第二天用券来拿。
所以每天吃的生煎来源于前一天买的券,和当天一次性两个两个买的。

在遍历每一天的过程中,如果用不完前一天的券那么就不能做到咕咕东的要求。在最后一天若还有券剩余也达不到要求。

完整代码

#include <iostream>
using namespace std;
int n;
int a[100100];
int main()
{
	cin>>n;
	for(int i=0;i<n;i++)
	{
		cin>>a[i];
	}
	int k = 0;
	int quan = 0;
	for(int i=0;i<n;i++)
	{
		if(a[i]-quan<0)	
		{
			k = 1;
			break;
		}
		quan = (a[i]-quan)%2;
	}
	if(quan!=0)	k = 1;
	if(k==0)	cout<<"YES"<<endl;
	else if(k==1)	cout<<"NO"<<endl;
	return 0;
}

总结

读懂题意后这题并不难,但是不细心看清楚题目就可能翻车了QAQ。在比赛的时候这题只拿了70分,,,,就是因为误读了题意,当时以为每天只能选择一个方案使用,只可以买0个、1个或者2个,后来才知道方案可重复什么的,痛失30分qwq。

C - 可怕的宇宙射线

题目描述

在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述题解

折磨我一晚错误提交十几次的题目hhhhhh

这一题乍一看就想到用bfs搜索,遍历看到达了哪些点,然后做上标记计数。。。虽说题目给出分裂次数不超过30次,但是每次要分裂成两个方向,这可是一个指数级问题,要炸了qwq。

所以一定不能纯bfs,需要剪枝。想一下这个分裂移动的过程,搜索到每一层每个终点都要分裂成两个方向,以给出的步长移动,有没有什么情况是重复可以剪掉避免重复搜索的呢?

显然,并不是搜到相同位置的点就可以去掉了,因为即使在同一个位置还可能有八个不同的方向,方向不同,分裂的方向就不相同。
但是即便是同一位置并且方向相同的情况也不可以剪掉,因为这只能保证分裂后方向一致,而每一层分裂后要移动的步长并不一定相等,这会导致不同的结果。
所以,同一位置、方向相同并且在同一层分裂才属于重复可剪去的情况。

这一题的关键点就在剪枝这里,如果不选择性的去除重复情况,数据一大就会超空间。看样例这种小数据看不出来什么,这是指数增长的问题。仔细一想就会发现数据一大,前面提到的剪枝要剪掉的情况出现次数并不少,同位置同方向且在同一层分裂,剪去会节省很多空间和时间。

剪枝想清楚思路理好,就写bfs好了,在push进队列前加判断条件,代码就不多解释了。

完整代码

#include <iostream>
#include <string.h>
#include <cstring>
#include <cmath>
#include <queue>
#include <map>
using namespace std;
struct p
{
    int x, y;
    int cc;
    int dir; // 1 2 3 4 上下左右  5 6 7 8
    bool operator < (const p &o) const
    {
        if(x<o.x) return true;
        if(y<o.y) return true;
        if(cc<o.cc) return true;
        if(dir<o.dir) return true;
    }
};
queue<p> q;
bool jd[600][600];
p aa[600][600];
map<p,bool> mp;
int transx[9] = {0, 0, 0, -1, 1, -1, 1, -1, 1};
int transy[9] = {0, 1, -1, 0, 0, 1, 1, -1, -1};
int nextdir1[9] = {0, 5, 7, 5, 6, 1, 1, 3, 4};
int nextdir2[9] = {0, 6, 8, 7, 8, 3, 4, 2, 2};
int n, ff, rr;
int a[100];
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
    }
    ff = 0;
    rr = 0;
    p first_p;
    first_p.x = 300;
    first_p.y = 300;
    first_p.cc = 1;
    first_p.dir = 1;

    jd[first_p.x][first_p.y] = 1;
    for(int g = 1;g<=a[1]-1;g++)
    {
        first_p.y +=1;
        jd[first_p.x][first_p.y] = 1;
    }
	aa[first_p.x][first_p.y] = first_p;
    q.push(first_p);
    int kk = first_p.cc;
	while(!q.empty())
    {
        p now_p = q.front();
        q.pop();
        p next_p1 = now_p;
        p next_p2 = now_p;
        next_p1.cc++;
        next_p2.cc++;
        next_p1.dir = nextdir1[now_p.dir];
        next_p2.dir = nextdir2[now_p.dir];
        if(next_p1.cc>n)
			break;
        for(int g=1;g<=a[next_p1.cc];g++)
        {
            next_p1.x += transx[next_p1.dir];
            next_p1.y += transy[next_p1.dir];
            jd[next_p1.x][next_p1.y] = 1;
            next_p2.x += transx[next_p2.dir];
            next_p2.y += transy[next_p2.dir];
            jd[next_p2.x][next_p2.y] = 1;
        }
		if(!((aa[next_p1.x][next_p1.y].cc == next_p1.cc) && (aa[next_p1.x][next_p1.y].dir == next_p1.dir)))
		{
			q.push(next_p1);
			aa[next_p1.x][next_p1.y] = next_p1;
		}
		if(!((aa[next_p2.x][next_p2.y].cc == next_p2.cc) && (aa[next_p2.x][next_p2.y].dir == next_p2.dir)))
		{
			q.push(next_p2);
			aa[next_p2.x][next_p2.y] = next_p2;
		}
    }
    int ans=0;
    for(int i=0;i<600;i++)
        for(int j=0;j<600;j++)
            if(jd[i][j]==1)
                ans++;
    cout<<ans<<endl;
    return 0;
} 

总结

在想搜索问题的时候要想想是否可以剪枝,去掉重复搜索的情况,特别是数据范围大的题目。一开始写这题时就没有想全面,直接mle了,在想剪枝时也出现了很多错误,判断错了重复情况或者只去掉了一小部分,反复mle了很多次。然后想清楚去重后,使用了map查找判断给tle了qwq。。。菜菜摸了一晚之后终于给调出来了。。。码力欠费,一遇到复杂的问题就容易乱,很多时候就是不知道怎么写出来QAQ。

发布了10 篇原创文章 · 获赞 3 · 访问量 755

猜你喜欢

转载自blog.csdn.net/wakeupshely/article/details/104865986