2020第十一届蓝桥杯大赛软件类国赛 C/C++ 大学 B 组

成绩是国二中游,赛场上填空题写了第1,2,5题,第2题大意错了,编程题写了第7,8,10题,第7题对了样例,后两题不知道可以对多少,总分有50左右吧。

试题 A: 美丽的 2

本题总分:5 分
【问题描述】
小蓝特别喜欢 2,今年是公元 2020 年,他特别高兴。
他很好奇,在公元 1 年到公元 2020 年(包含)中,有多少个年份的数位中包含数字 2?

签到题。答案563

#include<cstdio>
using namespace std;
int n,m,ans;
int main()
{
	int i,j;
	for(i=1;i<=2020;i++){
		j=i;
		while(j){
			if(j%10==2){
				ans++;
				printf("%d ",i);
				break;
			}
			j/=10;
		}
	} 
	printf("\n%d",ans);
	return 0;
} 
//563

试题 B: 扩散

本题总分:5 分
【问题描述】
小蓝在一张无限大的特殊画布上作画。
这张画布可以看成一个方格图,每个格子可以用一个二维的整数坐标表示。
小蓝在画布上首先点了一下几个点:(0, 0), (2020, 11), (11, 14), (2000, 2000)。
只有这几个格子上有黑色,其它位置都是白色的。
每过一分钟,黑色就会扩散一点。具体的,如果一个格子里面是黑色,它就会扩散到上、下、左、右四个相邻的格子中,使得这四个格子也变成黑色(如果原来就是黑色,则还是黑色)。
请问,经过 2020 分钟后,画布上有多少个格子是黑色的。

我的是bfs从四个点搜索的,看网上大部分的答案不是这个:11157392
emmm和同学交流后发现还有可能有负数的情况,
实现也不难,每个点的坐标加上2100,因为每次扩散1,向后扩散2020次,2100够用了,数组也扩大一点,然后扩散。这个答案是20312088

修改后的:

#include<cstdio>
#include<cstring>
#include<cmath>
#include<vector>
#include<queue>
using namespace std;
const int inf=0x3f3f3f3f;
const int N=10000;
typedef long long ll;
int n,m;
ll ans;
bool a[N][N];
struct node{
	int x,y,st;
}s,t;
int hy[4][2]={1,0, -1,0, 0,1, 0,-1};
void bfs(){
	int i;
	queue<node>q;
	q.push((node){0+2100,0+2100,0});
	q.push((node){2020+2100,11+2100,0});
	q.push((node){11+2100,14+2100,0});
	q.push((node){2000+2100,2000+2100,0});
	a[0+2100][0+2100]=a[2020+2100][11+2100]=a[11+2100][14+2100]=a[2000+2100][2000+2100]=1;
	ans=4;
	while(!q.empty()){
		t=q.front();
		q.pop();		
		s.st=t.st+1;
		for(int i=0;i<4;i++){
		 	s.x=t.x+hy[i][0];
		 	s.y=t.y+hy[i][1];
		 	if(s.x>=0&&s.y>=0&&a[s.x][s.y]==0&&s.st<=2020){
		 		a[s.x][s.y]=1;
		 		ans++;
		 		q.push(s);
		 	}
		}		
	}
	printf("%d",ans);
	
}
int main()
{
	int i,j;
	bfs();
	return 0;
} 
//20312088

试题 C: 阶乘约数

本题总分:10 分
【问题描述】
定义阶乘 n! = 1 × 2 × 3 × · · · × n。
请问 100! (100 的阶乘)有多少个约数。

其实就是把从那些因子中挑出来问你最多能组成多少个数。
为了防止2* 3=6的重复计算,我们不能直接挑,所以要先用唯一分解定理分解成素因子乘积的形式
如:5!=1* 2* 3* 4* 5=2^3 +3^1 +5^1;
所以现在的情况变成了:2有4种选择(0、1、2、3个),3有2种选择(0、1个),5有2种选择(0、1个).即每个素因子的选择个数是其幂次+1。
所以对于100!的答案就是把2-100的每一个数进行分解,记录下每一个素因子的个数,然后+1乘起来即可

#include<bits/stdc++.h>
using namespace std;
const int N=105;
typedef long long ll;
int a[N];
int main()
{
	int i,j,k,x,y;	
	for(i=2;i<=100;i++){
		x=i;
		for(j=2;j*j<=i;j++){
			while(x%j==0){
				a[j]++;
				x/=j;
			}
		}
		if(x!=1) a[x]++;
	}
	ll ans=1;
	for(i=1;i<=100;i++){
		if(a[i]) ans*=(a[i]+1);
	}
	printf("%lld",ans);
	return 0;
}

试题 D: 本质上升序列

本题总分:10 分
【问题描述】
小蓝特别喜欢单调递增的事物。
在一个字符串中,如果取出若干个字符,将这些字符按照在字符串中的顺序排列后是单调递增的,则成为这个字符串中的一个单调递增子序列。
例如,在字符串 lanqiao 中,如果取出字符 n 和 q,则 nq 组成一个单调递增子序列。类似的单调递增子序列还有 lnq、i、ano 等等。
小蓝发现,有些子序列虽然位置不同,但是字符序列是一样的,例如取第二个字符和最后一个字符可以取到 ao,取最后两个字符也可以取到 ao。小蓝认为他们并没有本质不同。
对于一个字符串,小蓝想知道,本质不同的递增子序列有多少个?
例如,对于字符串 lanqiao,本质不同的递增子序列有 21 个。它们分别是 l、a、n、q、i、o、ln、an、lq、aq、nq、ai、lo、ao、no、io、lnq、anq、lno、ano、aio。
请问对于以下字符串(共 200 个小写英文字母,分四行显示):

tocyjkdzcieoiodfpbgcncsrjbhmugdnojjddhllnofawllbhf
iadgdcdjstemphmnjihecoapdjjrprrqnhgccevdarufmliqij
gihhfgdcmxvicfauachlifhafpdccfseflcdgjncadfclvfmad
vrnaaahahndsikzssoywakgnfjjaihtniptwoulxbaeqkqhfwl

本质不同的递增子序列有多少个?
在这里插入图片描述

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
using namespace std;
#define mk make_pair 
const int inf=0x3f3f3f3f;
typedef long long ll;
int n,m,ans;
char s[500]="tocyjkdzcieoiodfpbgcncsrjbhmugdnojjddhllnofawllbhfiadgdcdjstemphmnjihecoapdjjrprrqnhgccevdarufmliqijgihhfgdcmxvicfauachlifhafpdccfseflcdgjncadfclvfmadvrnaaahahndsikzssoywakgnfjjaihtniptwoulxbaeqkqhfwl";
map<string,int>vis;
int main()
{
	int i,j,k;
	queue<pair<string,int> >q;
	for(i=0;i<strlen(s);i++){
		string str="";
		str+=s[i];
		if(vis[str]==0){
			vis[str]=1;
			q.push(mk(str,i));
			ans++;
		}
	}
	while(!q.empty()){
		string str=q.front().first;
		int pos=q.front().second;
		q.pop();
		for(i=pos+1;i<200;i++){
			if(s[i]>s[pos]&&vis[str+s[i]]==0){
				vis[str+s[i]]=1;
				q.push(mk(str+s[i],i));
				ans++;
			}
		}
	}
	printf("%d",ans);
	return 0;
} 

试题 E: 玩具蛇

本题总分:15 分
【问题描述】
小蓝有一条玩具蛇,一共有 16 节,上面标着数字 1 至 16。每一节都是一个正方形的形状。相邻的两节可以成直线或者成 90 度角。小蓝还有一个 4 × 4 的方格盒子,用于存放玩具蛇,盒子的方格上依次标着字母 A 到 P 共 16 个字母。小蓝可以折叠自己的玩具蛇放到盒子里面。他发现,有很多种方案可以将玩具蛇放进去。
下图给出了两种方案:
在这里插入图片描述
请帮小蓝计算一下,总共有多少种不同的方案。如果两个方案中,存在玩
具蛇的某一节放在了盒子的不同格子里,则认为是不同的方案。

dfs四个方向搜索即可,答案552

#include<cstdio>
#include<cstring>
#include<cmath>
#include<vector>
#include<queue>
using namespace std;
const int inf=0x3f3f3f3f;
typedef long long ll;
int n=4,m,ans;
int v[5][5];
int hy[4][2]={1,0, -1,0, 0,1, 0,-1};
void dfs(int x,int y,int sum){
//	if(x<1||y<1||x>n||y>n) return ;
	if(sum==n*n){
		ans++;
		return;
	}
	for(int i=0;i<4;i++){
		int fx=x+hy[i][0];
		int fy=y+hy[i][1];
		if(fx<1||fy<1||fx>n||fy>n||v[fx][fy]==1) continue;
		v[fx][fy]=1;
		dfs(fx,fy,sum+1);
		v[fx][fy]=0;
	}
}
int main()
{
	int i,j;
	for(i=1;i<=n;i++){
		for(j=1;j<=n;j++){
			memset(v,0,sizeof(v));
			v[i][j]=1;
			dfs(i,j,1); 
		}
	}
	printf("%d",ans);
	return 0;
} 

试题 F: 皮亚诺曲线距离

时间限制: 1.0s 内存限制: 256.0MB 本题总分:15 分
【问题描述】
皮亚诺曲线是一条平面内的曲线。
下图给出了皮亚诺曲线的 1 阶情形,它是从左下角出发,经过一个 3×3 的方格中的每一个格子,最终到达右上角的一条曲线。
在这里插入图片描述

下图给出了皮亚诺曲线的 2 阶情形,它是经过一个 3 2 × 3 2 的方格中的每一个格子的一条曲线。它是将 1 阶曲线的每个方格由 1 阶曲线替换而成。
在这里插入图片描述

下图给出了皮亚诺曲线的 3 阶情形,它是经过一个 3 3 × 3 3 的方格中的每一个格子的一条曲线。它是将 2 阶曲线的每个方格由 1 阶曲线替换而成。( 最后一列倒数六和第七个点应该连着,图片有误)

皮亚诺曲线总是从左下角开始出发,最终到达右上角。我们将这些格子放到坐标系中,对于 k 阶皮亚诺曲线,左下角的坐标是(0,0),右上角坐标是 (3 k − 1,3 k − 1),右下角坐标是 (3 k − 1,0),左上角坐标是(0,3 k − 1)。给定 k 阶皮亚诺曲线上的两个点的坐标,请问这两个点之间,如果沿着皮亚诺曲线走,距离是到少?
【输入格式】
输入的第一行包含一个正整数 k,皮亚诺曲线的阶数。
第二行包含两个整数 x 1 , y 1 ,表示第一个点的坐标。
第三行包含两个整数 x 2 , y 2 ,表示第二个点的坐标。
【输出格式】
输出一个整数,表示给定的两个点之间的距离。
【样例输入】

1
0 0
2 2

【样例输出】

8

【样例输入】

2
0 2
0 3

【样例输出】

13

【评测用例规模与约定】
对于 30% 的评测用例,0 ≤ k ≤ 10。
对于 50% 的评测用例,0 ≤ k ≤ 20。
对于所有评测用例,0 ≤ k ≤ 100, 0 ≤ x 1 ,y 1 , x 2 ,y 2 < 3 k , x 1 ,y 1 , x 2 ,y 2 ≤ 10 18 。
数据保证答案不超过 10 18 。

这次蓝桥不讲武德, 乱放题的顺序,不好写的放到前面了。还好赛场上我看了一下这题就跳过了。

试题 G: 游园安排

时间限制: 1.0s
内存限制: 256.0MB
本题总分:20 分
【问题描述】
L 星球游乐园非常有趣,吸引着各个星球的游客前来游玩。小蓝是 L 星球游乐园的管理员。为了更好的管理游乐园,游乐园要求所有的游客提前预约,小蓝能看到系统上所有预约游客的名字。每个游客的名字由一个大写英文字母开始,后面跟0 个或多个小写英文字母。游客可能重名。小蓝特别喜欢递增的事物。今天,他决定在所有预约的游客中,选择一部分游客在上午游玩,其他的游客都在下午游玩,在上午游玩的游客要求按照预约的顺序排列后,名字是单调递增的,即排在前面的名字严格小于排在后面的名字。
一个名字 A 小于另一个名字 B 是指:存在一个整数 i,使得 A 的前 i 个字母与 B 的前 i 个字母相同,且 A 的第 i+1 个字母小于 B 的第 i+1 个字母。(如果 A 不存在第 i + 1 个字母且 B 存在第 i + 1 个字母,也视为 A 的第 i + 1 个字母小于 B 的第 i + 1 个字母)
作为小蓝的助手,你要按照小蓝的想法安排游客,同时你又希望上午有尽量多的游客游玩,请告诉小蓝让哪些游客上午游玩。如果方案有多种,请输出上午游玩的第一个游客名字最小的方案。如果此时还有多种方案,请输出第一个游客名字最小的前提下第二个游客名字最小的方案。如果仍然有多种,依此类推选择第三个、第四个……游客名字最小的方案。
【输入格式】
输入包含一个字符串,按预约的顺序给出所有游客的名字,相邻的游客名字之间没有字符分隔。
【输出格式】
按预约顺序输出上午游玩的游客名单,中间不加任何分隔字符。
【样例输入】

WoAiLanQiaoBei

【样例输出】

AiLanQiao

【评测用例规模与约定】
对于 20% 的评测数据,输入的总长度不超过 20 个字母。
对于 50% 的评测数据,输入的总长度不超过 300 个字母。
对于 70% 的评测数据,输入的总长度不超过 10000 个字母。
对于所有评测数据,每个名字的长度不超过 10 个字母,输入的总长度不超过 1000000 个字母。

先把名字处理保存一下,然后名字就按最长上升子序列来。
修正过的代码:每个名字都会在保存状态的b数组中出现,保存每个名字在b数组出现的位置,通过逆序检索,就可以求出原来的最长上升子序列。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
#include<set>
using namespace std;
const int inf=0x3f3f3f3f;
typedef long long ll;
int n,m;
string s,a[100005],b[100005],c[100005];
int main()
{
	int i,j,cnt=0;
	cin>>s;
	for(i=0;i<s.length();i++){
		if(s[i]>='A'&&s[i]<='Z'){
			a[++cnt]+=s[i];
			i++;
			while(s[i]>='a'&&s[i]<='z'){
				a[cnt]+=s[i];
				i++;
			}
			i--;
		}
	}
//	for(i=1;i<=cnt;i++){	cout<<ans[i]<<endl;	}
	int sum=1;
	int p[100005];
	b[1]=a[1];
	p[1]=sum;
	for(i=2;i<=cnt;i++){
		if(a[i]>b[sum]) {
			b[++sum]=a[i];
			p[i]=sum;
		}
		else{
			m=lower_bound(b+1,b+1+sum,a[i])-b;
			b[m]=a[i];
			p[i]=m;
		} 
	} 
	int temp=sum;
	for(i=cnt;i>=1;i--){
		if(temp==0) break;
		if(p[i]==temp){
			c[temp]=a[i];
			temp--;
		}
	}
	for(i=1;i<=sum;i++){
		cout<<c[i];
	}
	return 0;
} 

赛场代码,保存记录写错了,希望能多给几分吧。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
#include<set>
using namespace std;
const int inf=0x3f3f3f3f;
typedef long long ll;
int n,m;
string s,a[1000005],b[1000005],c[1000005];
int main()
{
	int i,j,cnt=0;
	cin>>s;
	for(i=0;i<s.length();i++){
		if(s[i]>='A'&&s[i]<='Z'){
			a[++cnt]+=s[i];
			i++;
			while(s[i]>='a'&&s[i]<='z'){
				a[cnt]+=s[i];
				i++;
			}
			i--;
		}
	}
//	for(i=1;i<=cnt;i++){	cout<<ans[i]<<endl;	}
	int sum=1;
	c[1]=b[1]=a[1];
	for(i=2;i<=cnt;i++){
		if(a[i]>b[sum]) {
			c[++sum]=a[i];
			b[++sum]=a[i];
		}
		else{
			if(sum==1){
				*lower_bound(c+1,c+1+sum,a[i])=a[i];
			}
			*lower_bound(b+1,b+1+sum,a[i])=a[i];
		} 
	} 
	for(i=1;i<=sum;i++){
		cout<<c[i];
	}
	return 0;
} 

试题 H: 答疑

时间限制: 1.0s
内存限制: 256.0MB
本题总分:20 分
【问题描述】
有 n 位同学同时找老师答疑。每位同学都预先估计了自己答疑的时间。老师可以安排答疑的顺序,同学们要依次进入老师办公室答疑。
一位同学答疑的过程如下:

  1. 首先进入办公室,编号为 i 的同学需要 si 毫秒的时间。
  2. 然后同学问问题老师解答,编号为 i 的同学需要 ai 毫秒的时间。
  3. 答疑完成后,同学很高兴,会在课程群里面发一条消息,需要的时间可以忽略。
  4. 最后同学收拾东西离开办公室,需要 ei 毫秒的时间。一般需要 10 秒、20 秒或 30 秒,即 ei 取值为 10000,20000 或 30000。
    一位同学离开办公室后,紧接着下一位同学就可以进入办公室了。答疑从 0 时刻开始。老师想合理的安排答疑的顺序,使得同学们在课程群里面发消息的时刻之和最小。
    【输入格式】
    输入第一行包含一个整数 n,表示同学的数量。
    接下来 n 行,描述每位同学的时间。其中第 i 行包含三个整数 si, ai, ei,意义如上所述。
    【输出格式】
    输出一个整数,表示同学们在课程群里面发消息的时刻之和最小是多少。
    【样例输入】
3
10000 10000 10000
20000 50000 20000
30000 20000 30000

【样例输出】

280000

【样例说明】
按照 1, 3, 2 的顺序答疑,发消息的时间分别是 20000, 80000, 180000。
【评测用例规模与约定】
对于 30% 的评测用例,1 ≤ n ≤ 20。
对于 60% 的评测用例,1 ≤ n ≤ 200。
对于所有评测用例,1 ≤ n ≤ 1000,1 ≤ si ≤ 60000,1 ≤ ai ≤ 1000000,
ei ∈ {10000, 20000, 30000},即 ei 一定是 10000、20000、30000 之一。

这题用贪心的思想,我是排个序,先按每个人需要的总时间排,因为后面的人总是等着前面的人,相同的话对前两项数据和排,可以是答案尽可能小。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
const int inf=0x3f3f3f3f;
typedef long long ll;
int n,m;
ll ans=0,sum=0;
struct node{
	int z,s1,s2;
}s[1000];
int cmp(node a,node b){
	if(a.s2!=b.s2) return a.s2<b.s2;
	else return a.s1<b.s1;
}
int main()
{
	int i,j,x,y,z;
	scanf("%d",&n);
	for(i=0;i<n;i++){
		scanf("%d %d %d",&x,&y,&s[i].z);
		s[i].s1=x+y;
		s[i].s2=x+y+s[i].z;
	}
	sort(s,s+n,cmp);
	for(i=0;i<n;i++){
		ans+=s[i].s1;
		sum+=ans;
		ans+=s[i].z;
	}
	printf("%lld",sum);
	return 0;
} 

试题 I: 出租车

时间限制: 1.0s
内存限制: 256.0MB
本题总分:25 分
【问题描述】
小蓝在 L 市开出租车。
L 市的规划很规整,所有的路都是正东西向或者正南北向的,道路都可以看成直线段。东西向的道路互相平行,南北向的道路互相平行,任何一条东西向道路垂直于任何一条南北向道路。
从北到南一共有 n 条东西向道路,依次标号为 H1, H2, · · · , Hn。从西到东一共有 m 条南北向的道路,依次标号为 S 1, S 2, · · · , S m。
每条道路都有足够长,每一条东西向道路和每一条南北向道路都相交,Hi与 S j 的交叉路口记为 (i, j)。
从 H1 和 S 1 的交叉路口 (1, 1) 开始,向南遇到的路口与 (1, 1) 的距离分别是 h1, h2, · · · , hn−1,向东遇到路口与 (1, 1) 的距离分别是 w1, w2, · · · , wm−1。
道路的每个路口都有一个红绿灯。
时刻 0 的时候,南北向绿灯亮,东西向红灯亮,南北向的绿灯会持续一段时间(每个路口不同),然后南北向变成红灯,东西向变成绿灯,持续一段时间后,再变成南北向绿灯,东西向红灯。
已知路口 (i, j) 的南北向绿灯每次持续的时间为 gij,东西向的绿灯每次持续的时间为 ri j,红绿灯的变换时间忽略。
当一辆车走到路口时,如果是绿灯,可以直行、左转或右转。如果是红灯,可以右转,不能直行或左转。如果到路口的时候刚好由红灯变为绿灯,则视为看到绿灯,如果刚好由绿灯变为红灯,则视为看到红灯。
每段道路都是双向道路,道路中间有隔离栏杆,在道路中间不能掉头,只能在红绿灯路口掉头。掉头时不管是红灯还是绿灯都可以直接掉头。掉头的时间可以忽略。
小蓝时刻 0 从家出发。今天,他接到了 q 个预约的订单,他打算按照订单的顺序依次完成这些订单,就回家休息。中途小蓝不准备再拉其他乘客。
小蓝的家在两个路口的中点,小蓝喜欢用 x1, y1, x2, y2 来表示自己家的位置,即路口 (x1, y1) 到路口 (x2, y2) 之间的道路中点的右侧,保证两个路口相邻(中间没有其他路口)。请注意当两个路口交换位置时,表达的是路的不同两边,路中间有栏杆,因此这两个位置实际要走比较远才能到达。小蓝的订单也是从某两个路口间的中点出发,到某两个路口间的中点结束。小蓝必须按照给定的顺序处理订单,而且一个时刻只能处理一个订单,不能图省时间而同时接两位乘客,也不能插队完成后面的订单。小蓝只对 L 市比较熟,因此他只会在给定的 n 条东西向道路和 m 条南北向道路上行驶,而且不会驶出 H1, Hn, S 1, S m 这几条道路所确定的矩形区域(可以到边界)。小蓝行车速度一直为 1,乘客上下车的时间忽略不计。
请问,小蓝最早什么时候能完成所有订单回到家。
【输入格式】
输入第一行包含两个整数 n, m,表示东西向道路的数量和南北向道路的数
量。
第二行包含 n − 1 个整数 h1, h2, · · · , hn−1。
第三行包含 m − 1 个整数 w1, w2, · · · , wm−1。
接下来 n 行,每行 m 个整数,描述每个路口南北向绿灯的时间,其中的第
i 行第 j 列表示 gi j。
接下来 n 行,每行 m 个整数,描述每个路口东西向绿灯的时间,其中的第
i 行第 j 列表示 ri j。
接下来一行包含四个整数 x1, y1, x2, y2,表示小蓝家的位置在路口 (x1, y1)
到路口 (x2, y2) 之间的道路中点的右侧。
接下来一行包含一个整数 q,表示订单数量。
接下来 q 行,每行描述一个订单,其中第 i 行包含八个整数 xi1, yi1, xi2, yi2,
xi3, yi3, xi4, yi4,表示第 i 个订单的起点为路口 (xi1, yi1) 到路口 (xi2, yi2) 之间的道
路中点的右侧,第 i 个订单的终点为路口 (xi3, yi3) 到路口 (xi4, yi4) 之间的道路中
点的右侧。
【输出格式】
输出一个实数,表示小蓝完成所有订单最后回到家的最早时刻。四舍五入
保留一位小数。
【样例输入】

2 3
200
100 400
10 20 10
20 40 30
20 20 20
20 20 20
2 1 1 1
1
2 2 1 2 1 2 1 3

【样例输出】

1620.0

【样例说明】
小蓝有一个订单,他的行车路线如下图所示。其中 H 表示他家的位置,S
表示订单的起点,T 表示订单的终点。小明在最后回家时要在直行的红绿灯路
口等绿灯,等待时间为 20。
在这里插入图片描述

【评测用例规模与约定】
对于 20% 的评测用例,1 ≤ n, m ≤ 5,1 ≤ q ≤ 10。
对于 50% 的评测用例,1 ≤ n, m ≤ 30,1 ≤ q ≤ 30。
对于所有评测用例,1 ≤ n, m ≤ 100,1 ≤ q ≤ 30,1 ≤ h1 < h2 < · · · < hn−1 ≤
100000,1 ≤ w1 < w2 < · · · < wm−1 ≤ 100000,1 ≤ gij ≤ 1000,1 ≤ rij ≤ 1000,给
定的路口一定合法。

题面太长不想看。。。

试题 J: 质数行者

时间限制: 1.0s
内存限制: 256.0MB
本题总分:25 分
【问题描述】
小蓝在玩一个叫质数行者的游戏。
游戏在一个 n × m × w 的立体方格图上进行,从北到南依次标号为第 1 行到第 n 行,从西到东依次标号为第 1 列到第 m 列,从下到上依次标号为第 1 层到第 w 层。
小蓝要控制自己的角色从第 1 行第 1 列第 1 层移动到第 n 行第 m 列第 w层。每一步,他可以向东走质数格、向南走质数格或者向上走质数格。每走到一个位置,小蓝的角色要稍作停留。
在游戏中有两个陷阱,分别为第 r1 行第 c1 列第 h1 层和第 r2 行第 c2 列第h2 层。这两个陷阱的位置可以跨过,但不能停留。也就是说,小蓝不能控制角色某一步正好走到陷阱上,但是某一步中间跨过了陷阱是允许的。
小蓝最近比较清闲,因此他想用不同的走法来完成这个游戏。所谓两个走法不同,是指小蓝稍作停留的位置集合不同。
请帮小蓝计算一下,他总共有多少种不同的走法。
提示:请注意内存限制,如果你的程序运行时超过内存限制将不得分。
【输入格式】
输入第一行包含两个整数 n, m, w,表示方格图的大小。
第二行包含 6 个整数,r1, c1, h1, r2, c2, h2,表示陷阱的位置。
【输出格式】
输出一行,包含一个整数,表示走法的数量。答案可能非常大,请输出答案除以 1000000007 的余数。
【样例输入】

5 6 1
3 4 1 1 2 1

【样例输出】

11

【样例说明】 用 (r, c, h) 表示第 r 行第 c 列第 h 层,可能的走法有以下几种:

  1. (1, 1, 1) − (1, 3, 1) − (1, 6, 1) − (3, 6, 1) − (5, 6, 1)。
  2. (1, 1, 1) − (1, 3, 1) − (3, 3, 1) − (3, 6, 1) − (5, 6, 1)。
  3. (1, 1, 1) − (1, 3, 1) − (3, 3, 1) − (5, 3, 1) − (5, 6, 1)。
  4. (1, 1, 1) − (3, 1, 1) − (3, 3, 1) − (3, 6, 1) − (5, 6, 1)。
  5. (1, 1, 1) − (3, 1, 1) − (3, 3, 1) − (5, 3, 1) − (5, 6, 1)。
  6. (1, 1, 1) − (3, 1, 1) − (5, 1, 1) − (5, 3, 1) − (5, 6, 1)。
  7. (1, 1, 1) − (3, 1, 1) − (5, 1, 1) − (5, 4, 1) − (5, 6, 1)。
  8. (1, 1, 1) − (1, 4, 1) − (1, 6, 1) − (3, 6, 1) − (5, 6, 1)。
  9. (1, 1, 1) − (1, 6, 1) − (3, 6, 1) − (5, 6, 1)。
  10. (1, 1, 1) − (3, 1, 1) − (3, 6, 1) − (5, 6, 1)。
  11. (1, 1, 1) − (3, 1, 1) − (5, 1, 1) − (5, 6, 1)。

【评测用例规模与约定】
对于 30% 的评测用例 1 ≤ n, m, w ≤ 50。
对于 60% 的评测用例 1 ≤ n, m, w ≤ 300。
对于所有评测用例,1 ≤ n, m, w ≤ 1000,1 ≤ r1, r2 ≤ n, 1 ≤ c1, c2 ≤ m,1 ≤ h1, h2 ≤ w,陷阱不在起点或终点,两个陷阱不同。

记忆化搜索,有两个点不能走。写完代码样例一遍过,就是记录数据的三维数组不敢开到1000,只敢开到300,有一点分吧。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<vector>
#include<queue>
using namespace std;
const int inf=0x3f3f3f3f;
const int N=1005;
typedef long long ll;
int n,m,w,ans,f1,f2,f3,w1,w2,w3,cnt;
int a[N],p[N];
int f[305][305][305]; 
int judge(int x,int y,int z){
	if(x==f1&&y==f2&&z==f3) return 0;
	if(x==w1&&y==w2&&z==w3) return 0;
	return 1;
}
int dfs(int x,int y,int z,int sum){
	if(x==n&&y==m&&z==w) return 1;
	if(f[x][y][z]!=-1) return f[x][y][z];
	int c=0;
	for(int i=0;i<cnt;i++){
		if(x+p[i]<=n&&judge(x+p[i],y,z)){
			c+=dfs(x+p[i],y,z,sum);
		}
		if(y+p[i]<=m&&judge(x,y+p[i],z)){
			c+=dfs(x,y+p[i],z,sum);
		}
		if(z+p[i]<=w&&judge(x,y,z+p[i])){
			c+=dfs(x,y,z+p[i],sum);
		}
	}
	return f[x][y][z]=c;
}
int main()
{
	int i,j;
	scanf("%d %d %d",&n,&m,&w);
	scanf("%d %d %d",&f1,&f2,&f3);
	scanf("%d %d %d",&w1,&w2,&w3);
	for(i=2;i<=N;i++){
		if(a[i]==0){
			p[cnt++]=i;
			for(j=i+i;j<=N;j+=i){
				a[j]=1;
			}
		}
	}
	memset(f,-1,sizeof(f));
	dfs(1,1,1,0);
	printf("%d",f[1][1][1]);
	return 0;
} 
  • 59
    点赞
  • 274
    收藏
    觉得还不错? 一键收藏
  • 19
    评论
评论 19
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值