Week4-程序设计思维与实践(第一次CSP模拟)

A


题目描述

有一个由字母表组成的首尾相接的环,环上有一个指针,最初指向字母a。每次可以顺时针或者逆时针旋转一格。例如,a顺时针旋转到z,逆时针旋转到b。给定一个字符串,问最少需要转多少次。

思路

选取与当前指针指向字母的两种距离(顺 / 逆时针)最短的进行旋转,并在旋转后更新指针指向的字母。

代码实现

#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <cstdio>
using namespace std;
int curindex=0;
int Round(string s,char c){
	int dis1=c-s[curindex];
	dis1=abs(dis1);
	int dis2=c-s[curindex];
	dis2=abs(dis2);
	dis2=26-dis2;
	curindex=c-'a';
	return min(dis1,dis2);
}

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	string s="";
	char a='a';
	for(int i=0;i<26;i++){
		s+=a;
		a++;
	}
	string test;
	cin>>test;
	int ans=0;	 
	for(int i=0;i<test.size();i++){
		int num=Round(s,test[i]);
		ans+=num;
	}
	cout<<ans<<endl;
	return 0;
}

B


题目描述

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

思路

贪心算法的简单应用,对于第i天的ai优先使用前一天余下来的券pre,若余下的生煎数量 ai-pre<0则说明不满足条件,退出;若ai-pre>0再优先选取第一种购买方案,若使用方案一无法购买余下的全部生煎则再选取方案二来购买剩下的一个生煎。到最后一天时,若ai减去之前余下的券的结果为偶数则说明满足条件。

代码实现

#include <iostream>
using namespace std;

int n;
int a[100010];

int main()
{
	cin>>n;
	for(int i=0;i<n;i++)
		cin>>a[i];
	int pre=0;
	int cur=0;
	for(int i=0;i<n-1;i++){
		if(a[i]-pre<0){
			cout<<"NO"<<endl;
			return 0;
		}		
		cur=a[i]-pre;
		if(cur%2==1)
			pre=1;
		else
			pre=0;
	}
	if((a[n-1]-pre)%2==0)
		cout<<"YES"<<endl;
	else
		cout<<"NO"<<endl;
	return 0;
}

C


题目描述

宇宙射线会在无限的二维平面上传播(可以看做一个二维网格图),初始方向默认向上。宇宙射线会在发射出一段距离后分裂,向该方向的左右45°方向分裂出两条宇宙射线,同时威力不变!宇宙射线会分裂n次,每次分裂后会在分裂方向前进ai个单位长度,要求计算出共有多少个位置会被宇宙射线穿过。

 

思路

利用BFS统计宇宙射线所到达的方格个数,由于宇宙射线的分裂次数最大为30次,每次分裂后前进的单位长度最大为5,所以宇宙射线在每个方向上最多前进150个单位长度,但由于分裂点的个数是指数级增长的(远远大于150),故会有多个分裂方向相同的分裂点处于同一方格,可利用此条件来进行剪枝,为vis数组增加一维信息来记录通过该方格的宇宙射线的方向。但这样剪枝提交后会WA,因为由于ai(不同层射线的前进距离)的值是不一定相同的,这样可能会将不同层同一方向的分裂点剪去,所以最后再为vis数组增加一维信息来记录通过该方格的宇宙射线的层数,只减去同一层中处于同一方格且分裂方向相同的分裂点。

代码实现

#include <iostream>
#include <cstring>
#include <queue>

using namespace std;
int n;
int a[40];
struct node{
	int x;
	int y;
	int t;
	int level;
	node(){}
	node(int xx,int yy,int tt,int ll){
		x=xx;
		y=yy;
		t=tt;
		level=ll;
	}
};
struct transfer{
	int x1;
	int y1;
	int x2;
	int y2;
	transfer(){}
	transfer(int aa,int bb,int cc,int dd){
		x1=aa;y1=bb;x2=cc;y2=dd;
	}
};

transfer op[10];
bool grid[510][510];
bool vis[35][510][510][8];
queue<node> q;

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	op[0]=transfer(-1,1,1,1);
	op[1]=transfer(0,1,1,0);
	op[2]=transfer(1,1,1,-1);
	op[3]=transfer(1,0,0,-1);
	op[4]=transfer(1,-1,-1,-1);
	op[5]=transfer(0,-1,-1,0);
	op[6]=transfer(-1,-1,-1,1);
	op[7]=transfer(-1,0,0,1);
	int ans=0;
	cin>>n;
	for(int i=0;i<n;i++){
		cin>>a[i];
	}
	memset(vis,0,sizeof(vis));
	memset(grid,0,sizeof(grid));
	int sx,sy;
	sx=250;
	sy=250;
	int jumpnum=a[0];
	while(jumpnum--){
		sx+=0;
		sy+=1;
		grid[sx][sy]=1;
		ans++;
	}
	q.push(node(sx,sy,0,1));
	vis[1][sx][sy][0]=1;
	while(!q.empty()){
		node t=q.front();
		q.pop();
		if(t.level>=n)
			break;
		int steps=a[t.level];
		int dx,dy;
		dx=t.x;dy=t.y;
		while(steps--){
			dx+=op[t.t].x1;
			dy+=op[t.t].y1;
			if(!grid[dx][dy]){
				grid[dx][dy]=1;
				ans++;
			}
		}
		int op1=(t.t+7)%8;
		int op2=(t.t+1)%8;
		if(!vis[t.level+1][dx][dy][op1]){
			vis[t.level+1][dx][dy][op1]=1;
			q.push(node(dx,dy,op1,t.level+1));
		} 
		steps=a[t.level];
		dx=t.x;dy=t.y;
		while(steps--){
			dx+=op[t.t].x2;
			dy+=op[t.t].y2;
			if(!grid[dx][dy]){
				grid[dx][dy]=1;
				ans++;
			}
		}
		if(!vis[t.level+1][dx][dy][op2]){
			vis[t.level+1][dx][dy][op2]=1;
			q.push(node(dx,dy,op2,t.level+1));
		} 
	}
	cout<<ans<<endl;
	return 0;
}

总结

第一题签到题不再多说,第二题一个简单的贪心算法的应用给做上头了,读题“咕咕东是个节俭的好孩子”,然后在潜意识里认为方案二的花费最少,然后就.....稀里糊涂地转向了用DFS求解花费最少的可行解问题........当然由于这道题的剪枝条件不足,不出所料,提交后直接爆栈就拿了前两个测试点的分......第三题没有考虑到同一层的分裂点在同一方格分裂方向相同这一剪枝条件,妥妥的TLE。其实每次在比赛结束后再去静下心来分析每道题,思路就突然清晰,在比赛的过程中就很容易上头,还是要保持清醒的头脑,仔细解析题目所给条件,然后分析时间复杂度与程序可行性,选取正确的算法求解。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值