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

文章目录

  • 试题 A: 美丽的2 (5分)
    • 题解
  • 试题 B: 扩散 (5分)
    • 题解:
  • 试题 C: 阶乘约数 (10分)
    • 题解:
  • 试题 D: 本质上升序列 (10分)
    • 题解:
  • 试题 E: 玩具蛇 (15分)
    • 题解:
  • 试题 F: 皮亚诺曲线距离 (15分)
    • 题解:
  • 试题 G: 游园安排 (20分)
    • 题解:
  • 试题 H: 答疑 (20分)
    • 题解:
  • 试题 I: 出租车 (25分)
  • 试题 J: 质数行者 (25分)
    • 题解:

试题 A: 美丽的2 (5分)

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

题解

送分题 C++ 代码:

#include

using namespace std;

bool check(int x)
{
	while(x)
	{
		if(x % 10 == 2) return true;
		x /= 10;
	}
	return false;
}

int main()
{
	int res = 0;
	for(int i = 1; i <= 2020; i ++ )
		if(check(i)) res ++;
		
	cout << res << endl;
	return 0;
}
// 563

试题 B: 扩散 (5分)

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

题解:

本人刚看到这个题,第一反应就是 bfs, 但是着手开始写代码时发现了下面两个问题

  • 第一个问题:本题没有保证所有的点的坐标一定为正数,故存在 ( − x , − y ) (-x, -y) x,y 这种情况,解决途径想了一下大致有两个,如果用数组下标来存图的话就必须要加上最大可能会触及到点的偏移量,比如将 ( 0 , 0 ) (0, 0) (0,0) 点横纵坐标同时加上 2100(为什么是2100,其实只要比2020大就可以)变为 ( 2100 , 2100 ) (2100, 2100) (2100,2100) 这样在bfs是就不会有数组下标出现负数的情况了,第二个解决途径就是用pair 来存点的坐标,也可以做,我比较懒,就直接加偏移量了
  • 第二个问题:就是可能会有点会被重复计算,所以我们再bfs时必须加上一个 st[ ]状态数组,来看他是否已经被染过色了,完美解决了这个问题

C++代码:

#include 
#include 

#define x first
#define y second
 
using namespace std;
const int N = 10010;

struct node{
	int x, y, c;
}t, s;

queue<node> q;
bool st[N][N];
long long ans;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};

int main()
{
	q.push({0 + 2100, 0 + 2100, 0});
	q.push({2020 + 2100, 11 + 2100, 0});
	q.push({11 + 2100, 14 + 2100, 0});
	q.push({2000 + 2100, 2000 + 2100, 0});
	ans = 4;
	st[0 + 2100][0 + 2100] = st[2020 + 2100][11 + 2100] = true;
	st[11 + 2100][14 + 2100] = st[2000 + 2100][2000 + 2100] = true;
	
	while(!q.empty())
	{
    	t = q.front();
    	q.pop();
		s.c = t.c + 1;
        for(int i = 0; i < 4; i ++ )
        {
        	s.x = t.x + dx[i], s.y = t.y + dy[i];
        	if(s.x >= 0 && s.y >= 0 && !st[s.x][s.y] && s.c <= 2020)
        	{
        		st[s.x][s.y] = true;
        		ans ++;
        		q.push(s);	
			}	
		}
	}
	
	cout << ans << endl;
	
	return 0;
}
// 20312088

试题 C: 阶乘约数 (10分)

题目描述:
定义阶乘 n ! = 1 × 2 × 3 × ⋅ ⋅ ⋅ × n n! = 1 × 2 × 3 × · · · × n n!=1×2×3××n
请问 100 ! 100! 100! (100 的阶乘)有多少个正约数。

题解:

这个题会用到数论的知识,任意一个正整数 X 都可以表示成若干个质数乘积的形式,即 X = p 1 α 1 ∗ p 2 α 2 … … ∗ p k α k X = p_1^{α_1} ∗ p_2^{α_2} …… ∗ p_k^{α_k} X=p1α1p2α2pkαk

约数个数 S = ( a 1 + 1 ) ( a 2 + 1 ) … … ( a k + 1 ) S = (a_1 + 1)(a_2 + 1)……(a_k + 1) S=(a1+1)(a2+1)(ak+1)

C++代码:

#include 
#include 
#include 

using namespace std;
const int N = 110;
long long res = 1;
int a[N];  //预处理a数组就可以求出我们的指数序列

int main()
{
	for(int i = 2; i <= 100; i ++ )
	{
		int t = i;
		for(int j = 2; j <= t; j ++ )
		{
			//如果 t可以整除j 那么有关于j的约数个数 ++
			while(t % j == 0)
			{
				t /= j;
				a[j] ++;
			}
		}
	}
	
	for(int i = 2; i <= 100; i ++ )
		if(a[i]) res = res * (a[i] + 1);

	
	cout << res << endl;
	return 0;
}
// 39001250856960000

试题 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 个小写英文字母,分四行显示):(如果你把
以下文字复制到文本文件中,请务必检查复制的内容是否与文档中的一致。在
试题目录下有一个文件 inc.txt,内容与下面的文本相同)

tocyjkdzcieoiodfpbgcncsrjbhmugdnojjddhllnofawllbhf
iadgdcdjstemphmnjihecoapdjjrprrqnhgccevdarufmliqij
gihhfgdcmxvicfauachlifhafpdccfseflcdgjncadfclvfmad
vrnaaahahndsikzssoywakgnfjjaihtniptwoulxbaeqkqhfwl

本质不同的递增子序列有多少个?

题解:

我只想到了线性dp的做法:
f[i] 表示的就是以i结尾的前面所有的递增子串的个数
状态方程:

if(s[i] > s[j]) f[i] += f[j];

这里学要注意的就是题目中说到了有些子序列虽然位置不同,但是字符序列是一样的,例如取第二个字符和最后一个字符可以取到 ao,取最后两个字符也可以取到 ao。小蓝 所以必需要考虑会计算重复的问题,所以

if(s[i] == s[j]) f[i] -= f[j];

C++ 代码:

#include 

using namespace std;
const int N = 1000;

int f[N];
string ss;

int main()
{
	ss = "tocyjkdzcieoiodfpbgcncsrjbhmugdnojjddhllnofawllbhfiadgdcdjstemphmnjihecoapdjjrprrqnhgccevdarufmliqijgihhfgdcmxvicfauachlifhafpdccfseflcdgjncadfclvfmadvrnaaahahndsikzssoywakgnfjjaihtniptwoulxbaeqkqhfwl";

	for(int i = 0; i < ss.size(); i ++ )
	{
		f[i] = 1;
		for(int j = 0; j < i;j ++ )
		{
			if(ss[i] > ss[j]) f[i] += f[j];
			if(ss[i] == ss[j]) f[i] -= f[j];
		}
	}
	
	long long res = 0;
	for(int i = 0; i < ss.size(); i ++ ) res += f[i]; 
	cout << res << endl;
	return 0;
}
// 3616159

试题 E: 玩具蛇 (15分)

题目描述:
小蓝有一条玩具蛇,一共有 16 节,上面标着数字 1 至 16。每一节都是一个正方形的形状。相邻的两节可以成直线或者成 90 度角。小蓝还有一个 4 × 4 的方格盒子,用于存放玩具蛇,盒子的方格上依次标着字母 A 到 P 共 16 个字母。小蓝可以折叠自己的玩具蛇放到盒子里面。他发现,有很多种方案可以将玩具蛇放进去。
下图给出了两种方案:
第十一届蓝桥杯大赛软件类国赛 C/C++ 大学 B 组 试题+题解_第1张图片

请帮小蓝计算一下,总共有多少种不同的方案。如果两个方案中,存在玩具蛇的某一节放在了盒子的不同格子里,则认为是不同的方案。

题解:

本质就是dfs,这里有一个小技巧,很多时候,dfs和dp其实很像,导致我们常常拿不准到底用那种方法来解题,其实仔细想想,他们的差别其实很大,因为dp的话,是这个题可以分组考虑,而相反dfs就很多情况下是按个枚举,回到这个题,就比较有意思了,因为我们既可以考虑dp,也可以用dfs,这里推荐dfs,因为代码好懂。我们直接枚举蛇头的位置,然后开始dfs蛇身,当蛇身长度为16时,就可以答案++了, 在蓝桥杯中,这样的题算是送分题了。
C++ 代码:

#include 
#include 
#include 

using namespace std;
const int N = 5;

int ans;
bool st[N][N];

int dx[4] = {-1, 0, 1, 0} , dy[4] = {0, 1, 0, -1};

void dfs(int x, int y, int u)
{
	if(x >= 4 || y >= 4 || x < 0 || y < 0) return;
	if(u == 16) ans ++;
	
	for(int i = 0; i < 4; i ++ )
	{
		int tx = x + dx[i], ty = y + dy[i];
		if(!st[tx][ty])
		{
			st[tx][ty] = true;
			dfs(tx, ty, u + 1);
			st[tx][ty] = false;
		}
	}
}

int main()
{
	for(int i = 0; i < 4; i ++ )
		for(int j = 0; j < 4; j ++ )
		{
			memset(st, false, sizeof st);
			st[i][j] = true;
			dfs(i, j, 1);
			st[i][j] = false;
		}
	
	cout << ans << endl;
	return 0;
}
// 552

试题 F: 皮亚诺曲线距离 (15分)

题目描述:
皮亚诺曲线是一条平面内的曲线。
下图给出了皮亚诺曲线的 1 1 1 阶情形,它是从左下角出发,经过一个 3 × 3 3 × 3 3×3 的方格中的每一个格子,最终到达右上角的一条曲线。下图给出了皮亚诺曲线的2 阶情形,它是经过一个 3 2 × 3 2 3^2 × 3^2 32×32 的方格中的每一
个格子的一条曲线。它是将 1 阶曲线的每个方格由 1 阶曲线替换而成。
下图给出了皮亚诺曲线的 3 阶情形,它是经过一个 3 3 × 3 3 3^3 × 3^3 33×33 的方格中的每一个格子的一条曲线。它是将 2 阶曲线的每个方格由 1 阶曲线替换而成。皮亚诺曲线总是从左下角开始出发,最终到达右上角。我们将这些格子放到坐标系中,对于 k 阶皮亚诺曲线,
左下角的坐标是 ( 0 , 0 ) (0, 0) (0,0)
右上角坐标是 ( 3 k − 1 , 3 k − 1 ) (3^k − 1, 3^k − 1) (3k1,3k1)
右下角坐标是$ (3^k − 1, 0) , 左 上 角 坐 标 是 , 左上角坐标是 (0, 3^k − 1) $。
给定 k 阶皮亚诺曲线上的两个点的坐标,请问这两个点之间,如果沿着皮亚诺曲线走,距离是到少?
输入格式:
输入的第一行包含一个正整数 k,皮亚诺曲线的阶数。
第二行包含两个整数 x1, y1,表示第一个点的坐标。
输出格式
输出一个整数,表示给定的两个点之间的距离。
样例输入

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 ≤ 1018 0 ≤ k ≤ 100, 0 ≤ x1, y1, x2, y2 < 3^k, x1, y1, x2, y2 ≤1018 0k100,0x1,y1,x2,y2<3k,x1,y1,x2,y21018
数据保证答案不超过 1018。

题解:

这个题是找规律的题,不是很好做,我们慢慢来分析,大致思路就是把高阶的皮亚诺曲线映射为一阶皮亚诺,再去找不同皮亚诺的坐标变换。

  • 将高阶皮亚诺转化为一阶皮亚诺,可以考虑递归的思想,将高阶皮亚诺进行降阶,
	return ans + cacl(n - 1, pt);
  • 将要求坐标转化到一阶皮亚诺上
	//根据皮亚诺曲线的阶数,将要求确定的点映射
	//到一个一阶皮亚诺曲线上 
	LL len = pow(3, n - 1); 
	LL ans = 0;
	PII t(p.x / len, p.y / len);  //映射到一阶曲线上的点的坐标
	LL op = 0;
	LL tx = p.x, ty = p.y;
  • 最恶心的坐标转化
 // 根据皮亚诺曲线在一个九宫格中的分布,将每个格点进行分类,为九个点
	// 可以画图进行理解 
	if(t.x == 0) op = t.y + 1; 
	else if(t.x == 1)
	{
		if(t.y == 2) op = 4;
		if(t.y == 1) op = 5;
		if(t.y == 0) op = 6;	
	} 
	else op = t.y + 7;
	//就是从(0,0)点到 op所在格点要走的的步数 
	ans += len * len * (op - 1); 
	
	//递归结束 
	if(n == 1) return ans;
	
	PII pt;
	
	// 接下来的就是升阶后的坐标的转换,可以找,2阶和1阶对比来进行转化
	// 因为递归的存在,所以进行转化时,你所求点的坐标最后转化后就是以
	// 这个点所在的九宫格的左下角的那个点为(0,0),所做的转化 
	
	// 可以分组对称着来看 
	// 中心点  
	if(op == 5) pt = {-(tx - 2 * len + 1), - (ty - 2 * len + 1)};

	
	//正对角线 
	if(op == 3) pt = {ty, ty - 2 * len};
	if(op == 7) pt = {tx - 2 * len, ty};
	
	if(op == 2) pt = {-(tx - len + 1), ty - len};
	if(op == 6) pt = {tx - len, -(ty - len + 1)};
	
	
	//反对角线 
	if(op == 9) pt = {tx - 2 * len, ty - 2 * len};
	if(op == 1) pt = {tx, ty};
	
	if(op == 4) pt = {tx - len, -(ty - 3 * len + 1)};
	if(op == 8) pt = {-(tx - 3 * len + 1), ty - len};
	

C++ 代码:

#include 
#include 
#include 
#include  
 
#define x first
#define y second 
using namespace std;

typedef pair<int, int> PII;
typedef long long LL;

LL cacl(LL n, PII p)
{
	LL len = pow(3, n - 1); 
	LL ans = 0;
	PII t(p.x / len, p.y / len); 
	LL op = 0;
	LL tx = p.x, ty = p.y; 
	if(t.x == 0) op = t.y + 1; 
	else if(t.x == 1)
	{
		if(t.y == 2) op = 4;
		if(t.y == 1) op = 5;
		if(t.y == 0) op = 6;	
	} 
	else op = t.y + 7;
	ans += len * len * (op - 1); 
	if(n == 1) return ans;
	PII pt;
	if(op == 5) pt = {-(tx - 2 * len + 1), - (ty - 2 * len + 1)};
	if(op == 3) pt = {ty, ty - 2 * len};
	if(op == 7) pt = {tx - 2 * len, ty};
	if(op == 2) pt = {-(tx - len + 1), ty - len};
	if(op == 6) pt = {tx - len, -(ty - len + 1)};
	if(op == 9) pt = {tx - 2 * len, ty - 2 * len};
	if(op == 1) pt = {tx, ty};
	if(op == 4) pt = {tx - len, -(ty - 3 * len + 1)};
	if(op == 8) pt = {-(tx - 3 * len + 1), ty - len};
	
	return ans + cacl(n - 1, pt);
}

int main()
{
	LL n, x1, x2, y1, y2;
	scanf("%d%d%d%d%d", &n, &x1, &y1, &x2, &y2);
	PII fi(x1, y1), se(x2, y2);
	
	LL res = abs(cacl(n, fi) - cacl(n, se));
	
	cout << res << endl; 
	return 0;
}

试题 G: 游园安排 (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 个字母。

题解:

该题只想到了 O ( n 2 ) O(n^2) O(n2)的做法即线性dp,很明显只能拿一半的分,本题要拿满分肯定要是 O ( n ) 或 O ( n l o g n ) O(n) 或 O(nlog_n) O(n)O(nlogn)的做法,小编太菜了,还在努力学习中,日后如果可以想出满分做法,再来补充。那么开始考虑线性dp的做法

  • 首先应该考虑的是怎样将我们的每个名字存入到数组中,这就会难倒一些人了。
//处理输入的字符串,把每个名字存入字符串数组中 
   for(int i = 0; i < s.size(); i ++ )
   {
   //如果s[i]实在A~Z之间时,如果s[i]不是一个新名字的开头那就继续++,如果是的话
   //就放入到名字数组中idx++,并将t串赋空
   //如果s[i]  不在A~Z之间,就继续累加t串就可以
   	if(s[i] >= 'A' && s[i] <= 'Z' )
   	{
   		if(!fa) 
   			t += s[i];
   		else
   		{
   			e[idx ++] = t;
   			t = "";
   			t += s[i];
   			fa = false;
   		}
   		fa = true;
   	}
   	else t += s[i];
   	// 最后,不会出现大写字母了,仍要把累加的t串作为最后一个名字放入到名字数组中
   	if(i == s.size() - 1) e[idx ++] = t;
   }
  • 线性dp,直接看最核心的状态转移方程:f[i] = max(f[i], f[j] + 1),表示的是以s[i]结尾,前一个是s[j]的和当前已经求得的以s[i]结尾的中最大值。
  • 线性dp,准确来说是要记录子串的最大上升子序列,由于我们f[i]记录的是以i字母结尾的,i之前的最大上升子序列,那么可以用path[i] 来记录以i结尾的子串的前一个串,且满足上升序列要求的下标位置,最后在dfs求输出一下就可以了,或者可以循环输出。
#include 
#include 
#include 
#include 

using namespace std;
const int N =  1000010;

string e[N];
int idx;
bool fa = false;
int f[N], path[N];

void dfs(int u)
{
	if(u == 0) return;
	dfs(path[u]);
	cout<< e[u];
}

int main()
{
	string s, t; cin >> s;
	//处理输入的字符串,把每个名字存入字符串数组中 
	for(int i = 0; i < s.size(); i ++ )
	{
		if(s[i] >= 'A' && s[i] <= 'Z' )
		{
			if(!fa) 
				t += s[i];
			else
			{
				e[idx ++] = t;
				t = "";
				t += s[i];
				fa = false;
			}
			fa = true;
		}
		else t += s[i];
		if(i == s.size() - 1) e[idx ++] = t;
	}
	
	// dp 求最大上升子序列 
	int maxx = -1, pos;
	for(int i = 0; i < idx; i ++ )
	{
		f[i] = 1;
		for(int j = 0;j < i;j ++ )
		{
			if(e[j] < e[i]) 
			{
				f[i] = max(f[i], f[j] + 1);
				path[i] = j;
				if(maxx < f[i])
				{
					//记录位置 
					maxx = f[i];
					pos = i;
				}
			}	
		}
	} 
	
//	for(int i = 0 ;i < 100; i ++ )
//		if(path[i] != 0) cout << "i == " << i << " " << path[i] << endl;  
//	cout << "pos :" << pos << endl;
	// 遍历最大上升子序列 
	dfs(pos);
	
	return 0; 
}

试题 H: 答疑 (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 ≤ s i ≤ 60000 , 1 ≤ a i ≤ 1000000 1 ≤ n ≤ 1000,1 ≤ s_i ≤ 60000,1 ≤ a_i ≤ 1000000 1n10001si600001ai1000000
e i ∈ ( 10000 , 20000 , 30000 ) , 即 e i 一 定 是 10000 , 20000 , 30000 之 一 。 e_i ∈ (10000, 20000, 30000),即 e_i 一定是 10000,20000,30000 之一。 ei(10000,20000,30000)ei10000,20000,30000

题解:

贪心策略,很简单让总时间花费最小的先问,这样后面每个人等待的时间就最小,所以直接排序。时间复杂度 O ( n l o g n ) O(nlog_n) O(nlogn)应该是可以得到满分的。
C+代码:

#include 
#include 
#include 
#include 

typedef long long LL;
using namespace std;
const int N = 1010;

int n; 

struct tp{
	int a, b, c;
	int sum;
	bool operator < (const tp &tt) const
	{
		return sum < tt.sum;
	}
}t[N];
LL res = 0;

int main()
{
	cin >> n;
	int a, b, c;
	for(int i = 0; i < n; i ++ )
	{
		scanf("%d%d%d", &a, &b, &c);
		t[i] = {a, b, c, a + b + c};
	
	}
	sort(t, t + n);
	
	for(int i = 0 ; i < n; i ++) cout << t[i].sum << endl; 
	int mo[N] = {0};

	for(int i = 0; i < n; i ++ ) mo[i + 1] = t[i].sum + mo[i];
	for(int i = 0; i < n; i ++ ) res += t[i].a + t[i].b + mo[i];
	cout << res << endl;
	return 0;
}

试题 I: 出租车 (25分)

不会!跳过

试题 J: 质数行者 (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 对 于 60 对 于 所 有 评 测 用 例 , 1 ≤ n , m , w ≤ 1000 , 1 ≤ r 1 , r 2 ≤ n , 1 ≤ c 1 , c 2 ≤ m , 1 ≤ h 1 , h 2 ≤ w , 陷 阱 不 在 起 点 或 终 点 , 两 个 陷 阱 不 同 。 对于 30% 的评测用例 1 ≤ n, m,w ≤ 50。 对于 60% 的评测用例 1 ≤ n, m,w ≤ 300。 对于所有评测用例,1 ≤ n, m, w ≤ 1000,1 ≤ r_1,r_2 ≤ n, 1 ≤ c_1, c_2 ≤ m, 1 ≤ h_1, h_2 ≤ w,陷阱不在起点或终点,两个陷阱不同。 30601n,m,w10001r1,r2n,1c1,c2m,1h1,h2w

题解:

dfs + 素数筛
先通过素数筛求出 小于r,c,h 中最大值的所有质数,然后再三个方向进行dfs遍历就可以了,同样是只能拿部分分。

C++ 代码:

#include 
#include 

using namespace std;
const int N = 310, mod = 1e9 + 7;

int r1, c1, h1;
int r2, c2, h2;

int g[N][N][N];
bool st[N][N][N];
int n, m, w;

int primes[N], idx; 
bool stp[N]; 
int res;

void get_prime(int x)
{
	for(int i = 2; i <= n; i ++ )
	{
		if(!stp[i]) primes[idx ++ ] = i;
		for(int j = 0; primes[j] <= n / i; j ++ )
		{
			stp[primes[j] * i] = true;
			if(i % primes[j] == 0) break;		
		}
 	}
}

bool check(int x,int y,int h)
{
	if(x == r1 && y == c1 && h == h1) return false;
	if(x == r2 && y == c2 && h == h2) return false;
	return true;
}

void dfs(int x, int y, int h)
{
	if(x == n && y == m && h == w)
	{
		res = (res + 1) % mod;
		return;
	}
	
	for(int i = 0;i < idx; i ++ )
	{
		if(x + primes[i] <= n && check(x + primes[i], y, h))
		{
			st[x + primes[i]][y][h] = true;
			dfs(x + primes[i], y, h);
			st[x + primes[i]][y][h] = false;
		}
		if(y + primes[i] <= m && check(x, y + primes[i], h))
		{
			st[x][y + primes[i]][h] = true;
			dfs(x, y + primes[i], h);
			st[x][y + primes[i]][h] = false;
		}
		if(h + primes[i] <= h&& check(x, y, h + primes[i]))
		{
			st[x][y][h + primes[i]] = true;
			dfs(x, y, h + primes[i]);
			st[x][y][h + primes[i]] = false;
		}
	}
}

int main()
{
	scanf("%d%d%d", &n, &m, &w);
	scanf("%d%d%d%d%d%d", &r1, &c1, &h1, &r2, &c2, &h2);
	
	int maxx = max(max(n, m), w);
	get_prime(maxx);
	//for(int i = 0; i < maxx; i ++ ) cout << primes[i] << ' '; 
	
	st[1][1][1] = true;
	dfs(1, 1, 1);
	st[1][1][1] = false;
	
	cout << res << endl;
	return 0;
}

总结:自己太菜了

你可能感兴趣的:(算法总结,算法,动态规划,图论,经验分享)