第四周 CSP模拟zzh

博客讨论了两个问题:一是字符串旋转的最少量计算,采用贪心算法解决;二是判断特定披萨购买方式是否可行,通过分析奇偶性确定购买策略。还提及了宇宙射线问题的解决方案,通过记忆化广度优先搜索避免超时。
摘要由CSDN通过智能技术生成

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

对于这个问题的分析而言,我们可以简单的将其视为贪心算法,每次旋转的过程中,旋转的格数为 x 或 26-x ,我每一次都选择这两个数中小的那个。对于每次的字符处理而言,每次的旋转都相当于一个新的开始。因而不停的选择并累加两个数中较小的那个即可。

#include <iostream>
#include <cmath>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;

char test[10001];

int main()
{
	int now=0,sum=0;
	cin>>test;
	int n=strlen(test);         // n为字符串中字符的个数 
	 
	for(int i=0;i<n;i++)
	{
		 int t=test[i]-'a';
         int size=t-now;
		 if(size<0)
		 size=-1*size;
		 
		 int count=min(size,26-size);
		 sum=sum+count; 
		 
         now=t;
	}
	cout<<sum;
} 

2.特定购买方式的判断问题
一共有n天,然后每天需要买恰好ai个披萨,对于每天披萨的购买只有两种方式,要么一次买两个披萨,要么一次为今天买一个披萨同时为明天也买一个披萨,不能有其余任何的购买方式,且这两种购买方式可以使用无限次数,问是否每天都能恰好买到ai个。

思路:显然对于第i天的披萨,如果ai是偶数,则直接用第一种购买方式,因为这显然合法且对后面不产生影响,若ai是奇数,则应该先用第一种购买方式买到只剩一个,然后再用第二种购买方式。因此用sum标记当天有的券,且sum的取值只能是0或1,则当天购买的数目为ai-sum。假如购买数目为负,不合法并且结束程序,否则运行到最后之后判断sum是否为0,不为0则表明不合法。

#include <iostream>
using namespace std;

int main()
{
	int sum=0,n;

//	n=-1%2;
//	cout<<n;         此时的输出结果为 -1; 
	
	cin>>n;
	while(n>0)
	{
		int a;
		cin>>a;
		if((a-sum)%2==1) sum=1;            //一共三种情况,我将两种情况进行了统一 
		else if((a-sum)%2==-1) 
		{
		  cout<<"NO";
		  exit(0); 
	    }
		//else if((a-sum)%2==0) sum=0;
		else  sum=0;
		
		n--; 
	}
	
	if(sum==0) cout<<"YES";
	else cout<<"NO";
}

3.宇宙射线问题
单纯的递归会运行超时。
注意set容器与迭代器的使用,利用对称的思想使得有路径才会生成。
在讲师上课的时候,讲了一种方法为记忆化的广度优先搜索。那个我认为好理解也好实现。对于这个题目而言,每分裂一次,我们都进行遍历的话,很明显会超时。我利用无记忆递归进行实现时,在进行第五组测试时就会超时。
因而,我们除了进行记忆标记外。对于每次的分裂而言,我们只统计一条路径。然后这条路径到头后,我们利用8个方向的几何关系将该分支的相应对称分支进行标记。这样的话就实现了类似于记忆化搜索的效果。通过非重复化的标记访问,降低了时间复杂度。

#include<iostream>
#include<stdio.h>
#include<cmath>
#include<set>
using namespace std;


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


struct node 
{
	int x,y;
	int d;
	node()
	{
		x=0, y=0;
	}
	node(int x1, int y1)
	{
		x = x1;
		y = y1;
	}
};
bool operator <(const node &m,const node &n)
{
	if(m.x!=n.x) 
		return m.x<n.x;
	else
		return m.y<n.y;
	
}

bool operator ==(const node &m,const node &n)
{
		return m.x==n.x&&m.y==n.y;
}

set<node> way;

void number(int len, int n, int dir, const node& pos)
{
	if(len>=n)                 //len分裂次数,a[len],该次分裂长度 
		return;
	int x = pos.x, y = pos.y;
	x = x + dx[dir]*a[len];
	y = y + dy[dir]*a[len];
	node pos1(x,y);
	int dir1 = (dir+1)%8;
	number(len+1, n, dir1, pos1);
	x = pos.x, y = pos.y;
	for(int i=0;i<a[len];i++)
	{
		x = x + dx[dir];
		y = y + dy[dir];
		node pos2(x,y);
		way.insert(pos2); 
	}
	
	if(len!=0)//对称 
	{
		for(set<node>::iterator it=way.begin();it!=way.end();it++)
		{//将点对称 
			node now;
			if(dir==0||dir==4)
			{
				now.x=pos.x+pos.y-it->y;
				now.y=pos.x+pos.y-it->x;
			}
			else if(dir==1||dir==5)
			{
				now.x=2*pos.x-it->x;
				now.y=it->y;
			}
			else if(dir==2||dir==6)
			{
				now.x=it->y-pos.y+pos.x;
				now.y=it->x+pos.y-pos.x;
			}
			else if(dir==3||dir==7)
			{
				now.x=it->x;
				now.y=2*pos.y-it->y;
			}
			way.insert(now);
		}
	}
}

int main()
{
	int t;
	cin>>t;
	a = new int[t];
	node now(0,0);
	for(int i=0;i<t;i++)
		cin>>a[i];
	number( 0, t, 0, now);
	cout<<way.size()<<endl;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值