CCF-CSP 2016-2018

目录

CCF-CSP-2016-4-1 折点计数

CCF-CSP-2016-4-2 俄罗斯方块

CCF-CSP-2016-9-1 最大波动

CCF-CSP-2016-9-2 火车购票

CCF-CSP-2016-12-1 中间数

CCF-CSP-2016-12-2 工资计算

CCF-CSP-2016-12-4 压缩编码

CCF-CSP-2017-3-1 分蛋糕

CCF-CSP-2017-3-2 学生排队

CCF-CSP-2017-3-4 地铁修建(结构体优先队列)

CCF-CSP-2017-9-1 打酱油

CCF-CSP-2017-12-1 最小差值

CCF-CSP-2017-12-2 游戏

CCF-CSP-2018-3-1 跳一跳

CCF-CSP-2018-3-2 碰撞的小球

CCF-CSP-2018-3-3 URL映射

CCF-CSP-2018-3-4 棋局评估


CCF-CSP-2016-4-1 折点计数

题目:

问题描述

  给定n个整数表示一个商店连续n天的销售量。如果某天之前销售量在增长,而后一天销售量减少,则称这一天为折点,反过来如果之前销售量减少而后一天销售量增长,也称这一天为折点。其他的天都不是折点。如下图中,第3天和第6天是折点。

  给定n个整数a1, a2, …, an表示销售量,请计算出这些天总共有多少个折点。
  为了减少歧义,我们给定的数据保证:在这n天中相邻两天的销售量总是不同的,即ai-1≠ai。注意,如果两天不相邻,销售量可能相同。

输入格式

  输入的第一行包含一个整数n。
  第二行包含n个整数,用空格分隔,分别表示a1, a2, …, an。

输出格式

  输出一个整数,表示折点出现的数量。

样例输入

7
5 4 1 2 3 6 4

样例输出

2

评测用例规模与约定

  所有评测用例满足:1 ≤ n ≤ 1000,每天的销售量是不超过10000的非负整数。

代码:

#include 
using namespace std;
 
int main()
{
	int a, b, c, ans = 0;
	cin >> a >> a >> b;
	while (cin >> c)
	{
		ans += (b > a && b > c) + (b < a && b < c);
		a = b, b = c;
	}
	cout << ans;
	return 0;
}

CCF-CSP-2016-4-2 俄罗斯方块

题目:

问题描述

  俄罗斯方块是俄罗斯人阿列克谢·帕基特诺夫发明的一款休闲游戏。
  游戏在一个15行10列的方格图上进行,方格图上的每一个格子可能已经放置了方块,或者没有放置方块。每一轮,都会有一个新的由4个小方块组成的板块从方格图的上方落下,玩家可以操作板块左右移动放到合适的位置,当板块中某一个方块的下边缘与方格图上的方块上边缘重合或者达到下边界时,板块不再移动,如果此时方格图的某一行全放满了方块,则该行被消除并得分。
  在这个问题中,你需要写一个程序来模拟板块下落,你不需要处理玩家的操作,也不需要处理消行和得分。
  具体的,给定一个初始的方格图,以及一个板块的形状和它下落的初始位置,你要给出最终的方格图。

输入格式

  输入的前15行包含初始的方格图,每行包含10个数字,相邻的数字用空格分隔。如果一个数字是0,表示对应的方格中没有方块,如果数字是1,则表示初始的时候有方块。输入保证前4行中的数字都是0。
  输入的第16至第19行包含新加入的板块的形状,每行包含4个数字,组成了板块图案,同样0表示没方块,1表示有方块。输入保证板块的图案中正好包含4个方块,且4个方块是连在一起的(准确的说,4个方块是四连通的,即给定的板块是俄罗斯方块的标准板块)。
  第20行包含一个1到7之间的整数,表示板块图案最左边开始的时候是在方格图的哪一列中。注意,这里的板块图案指的是16至19行所输入的板块图案,如果板块图案的最左边一列全是0,则它的左边和实际所表示的板块的左边是不一致的(见样例)

输出格式

  输出15行,每行10个数字,相邻的数字之间用一个空格分隔,表示板块下落后的方格图。注意,你不需要处理最终的消行。

样例输入

0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 1 0 0
0 0 0 0 0 0 1 0 0 0
0 0 0 0 0 0 1 0 0 0
1 1 1 0 0 0 1 1 1 1
0 0 0 0 1 0 0 0 0 0
0 0 0 0
0 1 1 1
0 0 0 1
0 0 0 0
3

样例输出

0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 1 0 0
0 0 0 0 0 0 1 0 0 0
0 0 0 0 0 0 1 0 0 0
1 1 1 1 1 1 1 1 1 1
0 0 0 0 1 1 0 0 0 0

 

代码:

#include 
using namespace std;
 
int list[16][10], b[4][4], k, loc = 0;
 
bool ok(int r)
{
	for (int i = r; i < r + 4; i++)for (int j = k - 1; j < k + 3; j++)if (list[i][j] * b[i-r][j-k+1])return false;
	return true;
}
 
int main()
{
	for (int i = 0; i < 15; i++)for (int j = 0; j < 10; j++)cin >> list[i][j];
	for (int j = 0; j < 10; j++)list[15][j] = 1;
	for (int i = 0; i < 4; i++)for (int j = 0; j < 4; j++)cin >> b[i][j];
	cin >> k;
	for (int i = 1; i <= 14 && ok(i); i++)loc = i;
	for (int i = loc; i < loc + 4; i++)for (int j = k - 1; j < k + 3; j++)list[i][j] += b[i-loc][j-k+1];
	for (int i = 0; i < 15; i++)
	{
		for (int j = 0; j < 10; j++)cout << list[i][j] << " ";
		cout << endl;
	}
	return 0;
}

CCF-CSP-2016-9-1 最大波动

题目:

问题描述

  小明正在利用股票的波动程度来研究股票。小明拿到了一只股票每天收盘时的价格,他想知道,这只股票连续几天的最大波动值是多少,即在这几天中某天收盘价格与前一天收盘价格之差的绝对值最大是多少。

输入格式

  输入的第一行包含了一个整数n,表示小明拿到的收盘价格的连续天数。
  第二行包含n个正整数,依次表示每天的收盘价格。

输出格式

  输出一个整数,表示这只股票这n天中的最大波动值。

样例输入

6
2 5 5 7 3 5

样例输出

4

样例说明

  第四天和第五天之间的波动最大,波动值为|3-7|=4。

评测用例规模与约定

  对于所有评测用例,2 ≤ n ≤ 1000。股票每一天的价格为1到10000之间的整数。

代码:

#include 
using namespace std;
 
int main()
{
	int n, a, b, ans = 0;
	cin >> n >> a;
	while (--n)
	{
		cin >> b;
		if (ans < a - b)ans = a - b;
		if (ans < b - a)ans = b - a;
		a = b;
	}
	cout << ans;
	return 0;
}

CCF-CSP-2016-9-2 火车购票

题目:

问题描述

  请实现一个铁路购票系统的简单座位分配算法,来处理一节车厢的座位分配。
  假设一节车厢有20排、每一排5个座位。为方便起见,我们用1到100来给所有的座位编号,第一排是1到5号,第二排是6到10号,依次类推,第20排是96到100号。
  购票时,一个人可能购一张或多张票,最多不超过5张。如果这几张票可以安排在同一排编号相邻的座位,则应该安排在编号最小的相邻座位。否则应该安排在编号最小的几个空座位中(不考虑是否相邻)。
  假设初始时车票全部未被购买,现在给了一些购票指令,请你处理这些指令。

输入格式

  输入的第一行包含一个整数n,表示购票指令的数量。
  第二行包含n个整数,每个整数p在1到5之间,表示要购入的票数,相邻的两个数之间使用一个空格分隔。

输出格式

  输出n行,每行对应一条指令的处理结果。
  对于购票指令p,输出p张车票的编号,按从小到大排序。

样例输入

4
2 5 4 2

样例输出

1 2
6 7 8 9 10
11 12 13 14
3 4

样例说明

  1) 购2张票,得到座位1、2。
  2) 购5张票,得到座位6至10。
  3) 购4张票,得到座位11至14。
  4) 购2张票,得到座位3、4。

评测用例规模与约定

  对于所有评测用例,1 ≤ n ≤ 100,所有购票数量之和不超过100。

代码:

#include 
using namespace std;
 
int main()
{
	int num[20], n, a;
	for (int i = 0; i < 20; i++)num[i] = 5;
	cin >> n;
	while (n--)
	{
		cin >> a;
		for (int i = 0; i < 20 && a; i++)if (num[i] >= a)
		{
			for (int j = i * 5 + 6 - num[i]; j < i * 5 + 6 - num[i] + a; j++)cout << j << " ";
			num[i] -= a;
			a = 0;
		}
		while (a)for (int i = 0; i < 20; i++)if (num[i])
		{
			for (int j = i * 5 + 6 - num[i]; j <= i * 5 + 5; j++)cout << j << " ";
			a -= num[i];
			num[i] = 0;
		}
		cout << endl;
	}
	return 0;
}

CCF-CSP-2016-12-1 中间数

题目:

问题描述

  在一个整数序列a1, a2, …, an中,如果存在某个数,大于它的整数数量等于小于它的整数数量,则称其为中间数。在一个序列中,可能存在多个下标不相同的中间数,这些中间数的值是相同的。
  给定一个整数序列,请找出这个整数序列的中间数的值。

输入格式

  输入的第一行包含了一个整数n,表示整数序列中数的个数。
  第二行包含n个正整数,依次表示a1, a2, …, an。

输出格式

  如果约定序列的中间数存在,则输出中间数的值,否则输出-1表示不存在中间数。

样例输入

6
2 6 5 6 3 5

样例输出

5

样例说明

  比5小的数有2个,比5大的数也有2个。

样例输入

4
3 4 6 7

样例输出

-1

样例说明

  在序列中的4个数都不满足中间数的定义。

样例输入

5
3 4 6 6 7

样例输出

-1

样例说明

  在序列中的5个数都不满足中间数的定义。

评测用例规模与约定

  对于所有评测用例,1 ≤ n ≤ 1000,1 ≤ ai ≤ 1000。

代码:

#include 
#include
using namespace std;
 
int list[1001];
 
int main()
{
	int n;
	cin >> n;
	for (int i = 0; i < n; i++)cin >> list[i];
	sort(list, list + n);
	int mid = list[n / 2], num = 0;
	for (int i = 0; i < n; i++)if (list[i] == mid)num++;
	if (n % 2 != num % 2)mid = -1;
	cout << mid;
	return 0;
}

CCF-CSP-2016-12-2 工资计算

题目:

问题描述

  小明的公司每个月给小明发工资,而小明拿到的工资为交完个人所得税之后的工资。假设他一个月的税前工资(扣除五险一金后、未扣税前的工资)为S元,则他应交的个人所得税按如下公式计算:
  1) 个人所得税起征点为3500元,若S不超过3500,则不交税,3500元以上的部分才计算个人所得税,令A=S-3500元;
  2) A中不超过1500元的部分,税率3%;
  3) A中超过1500元未超过4500元的部分,税率10%;
  4) A中超过4500元未超过9000元的部分,税率20%;
  5) A中超过9000元未超过35000元的部分,税率25%;
  6) A中超过35000元未超过55000元的部分,税率30%;
  7) A中超过55000元未超过80000元的部分,税率35%;
  8) A中超过80000元的部分,税率45%;
  例如,如果小明的税前工资为10000元,则A=10000-3500=6500元,其中不超过1500元部分应缴税1500×3%=45元,超过1500元不超过4500元部分应缴税(4500-1500)×10%=300元,超过4500元部分应缴税(6500-4500)×20%=400元。总共缴税745元,税后所得为9255元。
  已知小明这个月税后所得为T元,请问他的税前工资S是多少元。

输入格式

  输入的第一行包含一个整数T,表示小明的税后所得。所有评测数据保证小明的税前工资为一个整百的数。

输出格式

  输出一个整数S,表示小明的税前工资。

样例输入

9255

样例输出

10000

评测用例规模与约定

  对于所有评测用例,1 ≤ T ≤ 100000。

 

代码:

#include 
using namespace std;
 
int f(int t)
{
	if (t <= 3500)return t;
	int s = 3500;
	t -= 3500;
	if (t <= 1455)return s + t * 100 / 97;
	s += 1500, t -= 1455;
	if (t <= 2700)return s + t * 100 / 90;
	s += 3000, t -= 2700;
	if (t <= 3600)return s + t * 100 / 80;
	s += 4500, t -= 3600;
	if (t <= 19500)return s + t * 100 / 75;
	s += 26000, t -= 19500;
	if (t <= 14000)return s + t * 100 / 70;
	s += 20000, t -= 14000;
	if (t <= 16250)return s + t * 100 / 65;
	s += 25000, t -= 16250;
	return s + t * 100 / 55;
}
 
int main()
{
	int t;
	cin >> t;
	cout << f(t);
	return 0;
}

CCF-CSP-2016-12-4 压缩编码

题目:

 

问题描述

  给定一段文字,已知单词a 1, a 2, …, a n出现的频率分别t 1, t 2, …, t n。可以用01串给这些单词编码,即将每个单词与一个01串对应,使得任何一个单词的编码(对应的01串)不是另一个单词编码的前缀,这种编码称为前缀码。
  使用前缀码编码一段文字是指将这段文字中的每个单词依次对应到其编码。一段文字经过前缀编码后的长度为:
  L=a 1的编码长度×t 1+a 2的编码长度×t 2+…+ a n的编码长度×t n。
  定义一个前缀编码为字典序编码,指对于1 ≤ i < n,a i的编码(对应的01串)的字典序在a i +1编码之前,即a 1, a 2, …, a n的编码是按字典序升序排列的。
  例如,文字E A E C D E B C C E C B D B E中, 5个单词A、B、C、D、E出现的频率分别为1, 3, 4, 2, 5,则一种可行的编码方案是A:000, B:001, C:01, D:10, E:11,对应的编码后的01串为1100011011011001010111010011000111,对应的长度L为3×1+3×3+2×4+2×2+2×5=34。
  在这个例子中,如果使用哈夫曼(Huffman)编码,对应的编码方案是A:000, B:01, C:10, D:001, E:11,虽然最终文字编码后的总长度只有33,但是这个编码不满足字典序编码的性质,比如C的编码的字典序不在D的编码之前。
  在这个例子中,有些人可能会想的另一个字典序编码是A:000, B:001, C:010, D:011, E:1,编码后的文字长度为35。
  请找出一个字典序编码,使得文字经过编码后的长度L最小。在输出时,你只需要输出最小的长度L,而不需要输出具体的方案。在上面的例子中,最小的长度L为34。

输入格式

  输入的第一行包含一个整数n,表示单词的数量。
  第二行包含n个整数,用空格分隔,分别表示a 1, a 2, …, a n出现的频率,即t 1, t 2, …, t n。请注意a 1, a 2, …, a n具体是什么单词并不影响本题的解,所以没有输入a 1, a 2, …, a n。

输出格式

  输出一个整数,表示文字经过编码后的长度L的最小值。

样例输入

5
1 3 4 2 5

样例输出

34

样例说明

  这个样例就是问题描述中的例子。如果你得到了35,说明你算得有问题,请自行检查自己的算法而不要怀疑是样例输出写错了。

评测用例规模与约定

  对于30%的评测用例,1 ≤ n ≤ 10,1 ≤ t i ≤ 20;
  对于60%的评测用例,1 ≤ n ≤ 100,1 ≤ t i ≤ 100;
  对于100%的评测用例,1 ≤ n ≤ 1000,1 ≤ t i ≤ 10000。

 

在写动态规划的备忘录方法时需要注意一点,答案的最大值很接近接近10^8,而不是10^7

在我的代码中,我用m来表示这个数

代码:

#include 
using namespace std;
 
int list[1000], ans[1000][1000], m = 100000000, s[1000];
 
int f(int l, int r)
{
	if (l == r)return 0;
	if (ans[l][r] < m)return ans[l][r];
	for (int i = l; i < r; i++)
	{
		int result = f(l, i) + f(i+1, r) + s[r] - s[l] + list[l];
		if (ans[l][r]>result)ans[l][r] = result;
	}
	return ans[l][r];
}
 
int main()
{
	int n;
	cin >> n;
	for (int i = 0; i < n; i++)cin >> list[i];
	s[0] = list[0];
	for (int i = 1; i < n; i++)s[i] = s[i - 1] + list[i];
	for (int i = 0; i < n; i++)for (int j = 0; j < n; j++)ans[i][j] = m;
	cout << f(0, n - 1);
	return 0;
}

CCF-CSP-2017-3-1 分蛋糕

题目:

 

问题描述

  小明今天生日,他有n块蛋糕要分给朋友们吃,这n块蛋糕(编号为1到n)的重量分别为a1, a2, …, an。小明想分给每个朋友至少重量为k的蛋糕。小明的朋友们已经排好队准备领蛋糕,对于每个朋友,小明总是先将自己手中编号最小的蛋糕分给他,当这个朋友所分得蛋糕的重量不到k时,再继续将剩下的蛋糕中编号最小的给他,直到小明的蛋糕分完或者这个朋友分到的蛋糕的总重量大于等于k。
  请问当小明的蛋糕分完时,总共有多少个朋友分到了蛋糕。

输入格式

  输入的第一行包含了两个整数n, k,意义如上所述。
  第二行包含n个正整数,依次表示a1, a2, …, an。

输出格式

  输出一个整数,表示有多少个朋友分到了蛋糕。

样例输入

6 9
2 6 5 6 3 5

样例输出

3

样例说明

  第一个朋友分到了前3块蛋糕,第二个朋友分到了第4、5块蛋糕,第三个朋友分到了最后一块蛋糕。

评测用例规模与约定

  对于所有评测用例,1 ≤ n ≤ 1000,1 ≤ k ≤ 10000,1 ≤ ai ≤ 1000。

 

代码:

#include 
using namespace std;
 
int main()
{
	int n, k, ans = 0, s = 0, a;
	cin >> n >> k;
	while (n--)
	{
		cin >> a;
		s += a;
		if (s >= k)
		{
			s = 0;
			ans++;
		}
	}
	if (s)ans++;
	cout << ans;
	return 0;
}

CCF-CSP-2017-3-2 学生排队

题目:

问题描述

  体育老师小明要将自己班上的学生按顺序排队。他首先让学生按学号从小到大的顺序排成一排,学号小的排在前面,然后进行多次调整。一次调整小明可能让一位同学出队,向前或者向后移动一段距离后再插入队列。
  例如,下面给出了一组移动的例子,例子中学生的人数为8人。
  0)初始队列中学生的学号依次为1, 2, 3, 4, 5, 6, 7, 8;
  1)第一次调整,命令为“3号同学向后移动2”,表示3号同学出队,向后移动2名同学的距离,再插入到队列中,新队列中学生的学号依次为1, 2, 4, 5, 3, 6, 7, 8;
  2)第二次调整,命令为“8号同学向前移动3”,表示8号同学出队,向前移动3名同学的距离,再插入到队列中,新队列中学生的学号依次为1, 2, 4, 5, 8, 3, 6, 7;
  3)第三次调整,命令为“3号同学向前移动2”,表示3号同学出队,向前移动2名同学的距离,再插入到队列中,新队列中学生的学号依次为1, 2, 4, 3, 5, 8, 6, 7。
  小明记录了所有调整的过程,请问,最终从前向后所有学生的学号依次是多少?
  请特别注意,上述移动过程中所涉及的号码指的是学号,而不是在队伍中的位置。在向后移动时,移动的距离不超过对应同学后面的人数,如果向后移动的距离正好等于对应同学后面的人数则该同学会移动到队列的最后面。在向前移动时,移动的距离不超过对应同学前面的人数,如果向前移动的距离正好等于对应同学前面的人数则该同学会移动到队列的最前面。

输入格式

  输入的第一行包含一个整数n,表示学生的数量,学生的学号由1到n编号。
  第二行包含一个整数m,表示调整的次数。
  接下来m行,每行两个整数p, q,如果q为正,表示学号为p的同学向后移动q,如果q为负,表示学号为p的同学向前移动-q。

输出格式

  输出一行,包含n个整数,相邻两个整数之间由一个空格分隔,表示最终从前向后所有学生的学号。

样例输入

8
3
3 2
8 -3
3 -2

样例输出

1 2 4 3 5 8 6 7

评测用例规模与约定

  对于所有评测用例,1 ≤ n ≤ 1000,1 ≤ m ≤ 1000,所有移动均合法。

 

代码:

#include 
using namespace std;
 
int main()
{
	int n, m, list[1001], loc[1001], p, q;
	cin >> n >> m;
	for (int i = 1; i <= n; i++)list[i] = i, loc[i] = i;
	while (m--)
	{
		cin >> p >> q;
		int d = q > 0 ? 1 : -1;
		for (int i = loc[p]; i != loc[p] + q; i += d)
		{
			list[i] = list[i + d];
			loc[list[i]] = i;
		}
		list[loc[p] + q] = p;
		loc[p] += q;
	}
	for (int i = 1; i < n; i++)cout << list[i] << " ";
	cout << list[n];
	return 0;
}

CCF-CSP-2017-3-4 地铁修建(结构体优先队列)

题目:

 

问题描述

  A市有n个交通枢纽,其中1号和n号非常重要,为了加强运输能力,A市决定在1号到n号枢纽间修建一条地铁。
  地铁由很多段隧道组成,每段隧道连接两个交通枢纽。经过勘探,有m段隧道作为候选,两个交通枢纽之间最多只有一条候选的隧道,没有隧道两端连接着同一个交通枢纽。
  现在有n家隧道施工的公司,每段候选的隧道只能由一个公司施工,每家公司施工需要的天数一致。而每家公司最多只能修建一条候选隧道。所有公司同时开始施工。
  作为项目负责人,你获得了候选隧道的信息,现在你可以按自己的想法选择一部分隧道进行施工,请问修建整条地铁最少需要多少天。

输入格式

  输入的第一行包含两个整数n, m,用一个空格分隔,分别表示交通枢纽的数量和候选隧道的数量。
  第2行到第m+1行,每行包含三个整数a, b, c,表示枢纽a和枢纽b之间可以修建一条隧道,需要的时间为c天。

输出格式

  输出一个整数,修建整条地铁线路最少需要的天数。

样例输入

6 6
1 2 4
2 3 4
3 6 7
1 4 2
4 5 5
5 6 6

样例输出

6

样例说明

  可以修建的线路有两种。
  第一种经过的枢纽依次为1, 2, 3, 6,所需要的时间分别是4, 4, 7,则整条地铁线需要7天修完;
  第二种经过的枢纽依次为1, 4, 5, 6,所需要的时间分别是2, 5, 6,则整条地铁线需要6天修完。
  第二种方案所用的天数更少。

评测用例规模与约定

  对于20%的评测用例,1 ≤ n ≤ 10,1 ≤ m ≤ 20;
  对于40%的评测用例,1 ≤ n ≤ 100,1 ≤ m ≤ 1000;
  对于60%的评测用例,1 ≤ n ≤ 1000,1 ≤ m ≤ 10000,1 ≤ c ≤ 1000;
  对于80%的评测用例,1 ≤ n ≤ 10000,1 ≤ m ≤ 100000;
  对于100%的评测用例,1 ≤ n ≤ 100000,1 ≤ m ≤ 200000,1 ≤ a, b ≤ n,1 ≤ c ≤ 1000000。

  所有评测用例保证在所有候选隧道都修通时1号枢纽可以通过隧道到达其他所有枢纽。

 

代码:

#include 
#include
#include
using namespace std;
 
int n, m;
bool visit[100001];
struct dist
{
	int id;
	int len;
};
dist d, d2;
queueq[100001];
dist ans[100001];
struct cmp
{
	bool operator()(dist a, dist b)
	{
		return a.len > b.len;
	}
};
priority_queue, cmp>que;
 
int main()
{
	
	int a, b, c;
	cin >> n >> m;
	for (int i = 1; i <= n; i++)while (!q[i].empty())q[i].pop();
	for (int i = 1; i <= n; i++)
	{
		ans[i].id = i;
		ans[i].len = 1000000;
	}
	ans[1].len = 0;
	while (!que.empty())que.pop();
	for (int i = 1; i <= n; i++)que.push(ans[i]);
	for (int i = 1; i <= n; i++)visit[i] = false;
	while (m--)
	{
		scanf("%d%d%d", &a, &b, &c);
		d.id = b, d.len = c;
		q[a].push(d);
		d.id = a;
		q[b].push(d);
	}
	while (!que.empty())
	{
		d = que.top();
		que.pop();
		if (d.len > ans[d.id].len)continue;
		if (visit[d.id])continue;
		visit[d.id] = true;
		while (!q[d.id].empty())
		{
			d2 = q[d.id].front();
			q[d.id].pop();
			if (visit[d2.id])continue;
			if (d2.len < ans[d.id].len)d2.len = ans[d.id].len;
			if (ans[d2.id].len>d2.len)ans[d2.id].len = d2.len;
			que.push(d2);
		}
	}
	cout << ans[n].len;
	return 0;
}

使用结构体优先队列的时候需要注意,修改结构体的成员变量优先队列并不会自动维护最大(小)堆,为了解决这个问题,我使用的方法是将修改后的结构体变量又插入优先队列,而在优先队列弹出一个元素时,直接把它和ans进行对比即可知道它是不是被修改的最后一个版本,如果不是最后一个版本的话,弹出它就行了,不进行任何操作,也不标记visit

所以,整个程序的复杂度是O(n+m),n是顶点数,m是边数

CCF-CSP-2017-9-1 打酱油

问题描述

  小明带着 N元钱去买酱油。酱油10块钱一瓶,商家进行促销,每买3瓶送1瓶,或者每买5瓶送2瓶。请问小明最多可以得到多少瓶酱油。

输入格式

  输入的第一行包含一个整数 N,表示小明可用于买酱油的钱数。 N是10的整数倍, N不超过300。

输出格式

  输出一个整数,表示小明最多可以得到多少瓶酱油。

样例输入

40

样例输出

5

样例说明

  把40元分成30元和10元,分别买3瓶和1瓶,其中3瓶送1瓶,共得到5瓶。

样例输入

80

样例输出

11

样例说明

  把80元分成30元和50元,分别买3瓶和5瓶,其中3瓶送1瓶,5瓶送2瓶,共得到11瓶。

 

代码:

#include
using namespace std;
 
int main()
{
	int n;
	cin >> n;
	cout << n / 10 + n / 50 * 2 + n % 50 / 30;
	return 0;
}

CCF-CSP-2017-12-1 最小差值

题目:

问题描述

  给定 n个数,请找出其中相差(差的绝对值)最小的两个数,输出它们的差值的绝对值。

输入格式

  输入第一行包含一个整数 n。
  第二行包含 n个正整数,相邻整数之间使用一个空格分隔。

输出格式

  输出一个整数,表示答案。

样例输入

5
1 5 4 8 20

样例输出

1

样例说明

  相差最小的两个数是5和4,它们之间的差值是1。

样例输入

5
9 3 6 1 3

样例输出

0

样例说明

  有两个相同的数3,它们之间的差值是0.

数据规模和约定

  对于所有评测用例,2 ≤  n ≤ 1000,每个给定的整数都是不超过10000的正整数。

 

代码:

#include
#include
using namespace std;
 
int main()
{
	int n, num[1000], ans = 10000;
	cin >> n;
	for (int i = 0; i < n; i++)cin >> num[i];
	sort(num, num + n);
	for (int i = 0; i < n - 1; i++)
		if (ans > num[i + 1] - num[i])ans = num[i + 1] - num[i];
	cout << ans;
	return 0;
}

CCF-CSP-2017-12-2 游戏

题目:

 

问题描述

  有 n个小朋友围成一圈玩游戏,小朋友从1至 n编号,2号小朋友坐在1号小朋友的顺时针方向,3号小朋友坐在2号小朋友的顺时针方向,……,1号小朋友坐在 n号小朋友的顺时针方向。
  游戏开始,从1号小朋友开始顺时针报数,接下来每个小朋友的报数是上一个小朋友报的数加1。若一个小朋友报的数为 k的倍数或其末位数(即数的个位)为 k,则该小朋友被淘汰出局,不再参加以后的报数。当游戏中只剩下一个小朋友时,该小朋友获胜。
  例如,当n=5, k=2时:
  1号小朋友报数1;
  2号小朋友报数2淘汰;
  3号小朋友报数3;
  4号小朋友报数4淘汰;
  5号小朋友报数5;
  1号小朋友报数6淘汰;
  3号小朋友报数7;
  5号小朋友报数8淘汰;
  3号小朋友获胜。

  给定 n和 k,请问最后获胜的小朋友编号为多少?

输入格式

  输入一行,包括两个整数 n和 k,意义如题目所述。

输出格式

  输出一行,包含一个整数,表示获胜的小朋友编号。

样例输入

5 2

样例输出

3

样例输入

7 3

样例输出

4

数据规模和约定

  对于所有评测用例,1 ≤  n ≤ 1000,1 ≤  k ≤ 9。

 

暴力

代码:

#include
using namespace std;
 
int main()
{
	int n, k, s, ans[1001];
	cin >> n >> k;
	s = n;
	for (int i = 1; i <= n; i++)ans[i] = i;
	for (int i = 1, key = 1; s > 1; i++, key++)
	{
		if (key > s)key = 1;
		if (i%k == 0 || i % 10 == k)
		{
			for (int j = key + 1; j <= s; j++)ans[j - 1] = ans[j];
			s--, key--;
		}
	}
	cout << ans[1];
	return 0;
}

CCF-CSP-2018-3-1 跳一跳

题目:

问题描述

  近来,跳一跳这款小游戏风靡全国,受到不少玩家的喜爱。
  简化后的跳一跳规则如下:玩家每次从当前方块跳到下一个方块,如果没有跳到下一个方块上则游戏结束。
  如果跳到了方块上,但没有跳到方块的中心则获得1分;跳到方块中心时,若上一次的得分为1分或这是本局游戏的第一次跳跃则此次得分为2分,否则此次得分比上一次得分多两分(即连续跳到方块中心时,总得分将+2,+4,+6,+8...)。
  现在给出一个人跳一跳的全过程,请你求出他本局游戏的得分(按照题目描述的规则)。

输入格式

  输入包含多个数字,用空格分隔,每个数字都是1,2,0之一,1表示此次跳跃跳到了方块上但是没有跳到中心,2表示此次跳跃跳到了方块上并且跳到了方块中心,0表示此次跳跃没有跳到方块上(此时游戏结束)。

输出格式

  输出一个整数,为本局游戏的得分(在本题的规则下)。

样例输入

1 1 2 2 2 1 1 2 2 0

样例输出

22

数据规模和约定

  对于所有评测用例,输入的数字不超过30个,保证0正好出现一次且为最后一个数字。

代码:

#include
using namespace std;
 
int main()
{
	int t, r = 0, s = 0;
	while (cin >> t && t > 0)if (t == 1)r = 1, s++; else r += 2 - r % 2, s += r;
	cout << s;
	return 0;
}

CCF-CSP-2018-3-2 碰撞的小球

题目:

问题描述

  数轴上有一条长度为L(L为偶数)的线段,左端点在原点,右端点在坐标L处。有n个不计体积的小球在线段上,开始时所有的小球都处在偶数坐标上,速度方向向右,速度大小为1单位长度每秒。
  当小球到达线段的端点(左端点或右端点)的时候,会立即向相反的方向移动,速度大小仍然为原来大小。
  当两个小球撞到一起的时候,两个小球会分别向与自己原来移动的方向相反的方向,以原来的速度大小继续移动。
  现在,告诉你线段的长度L,小球数量n,以及n个小球的初始位置,请你计算t秒之后,各个小球的位置。

提示

  因为所有小球的初始位置都为偶数,而且线段的长度为偶数,可以证明,不会有三个小球同时相撞,小球到达线段端点以及小球之间的碰撞时刻均为整数。
  同时也可以证明两个小球发生碰撞的位置一定是整数(但不一定是偶数)。

输入格式

  输入的第一行包含三个整数n, L, t,用空格分隔,分别表示小球的个数、线段长度和你需要计算t秒之后小球的位置。
  第二行包含n个整数a1, a2, …, an,用空格分隔,表示初始时刻n个小球的位置。

输出格式

  输出一行包含n个整数,用空格分隔,第i个整数代表初始时刻位于ai的小球,在t秒之后的位置。

样例输入

3 10 5
4 6 8

样例输出

7 9 9

样例说明

  初始时,三个小球的位置分别为4, 6, 8。

  一秒后,三个小球的位置分别为5, 7, 9。

  两秒后,第三个小球碰到墙壁,速度反向,三个小球位置分别为6, 8, 10。

  三秒后,第二个小球与第三个小球在位置9发生碰撞,速度反向(注意碰撞位置不一定为偶数),三个小球位置分别为7, 9, 9。

  四秒后,第一个小球与第二个小球在位置8发生碰撞,速度反向,第三个小球碰到墙壁,速度反向,三个小球位置分别为8, 8, 10。

  五秒后,三个小球的位置分别为7, 9, 9。

样例输入

10 22 30
14 12 16 6 10 2 8 20 18 4

样例输出

6 6 8 2 4 0 4 12 10 2

数据规模和约定

  对于所有评测用例,1 ≤ n ≤ 100,1 ≤ t ≤ 100,2 ≤ L ≤ 1000,0 < ai < L。L为偶数。
  保证所有小球的初始位置互不相同且均为偶数。

思路:

每次2个小球碰撞,都向相反的方向运动,可以考虑成,2个球直接互相穿过,互不影响。

这样,所有的球都是各自独立运动,可以直接求出每个球最后的位置。

最后只需要把最开始的球和最后的球进行匹配即可,因为球和球之间是左右顺序是不会变的,

所以(只需要把最后所有球的位置排序)根据这个指标就可以直接求出来。

代码:

#include
#include
using namespace std;
 
int main()
{
	int n, l, t, loc[101], ans[101], temp;
	cin >> n >> l >> t;
	t %= l * 2;
	for (int i = 1; i <= n; i++)cin >> loc[i];
	for (int i = 1; i <= n; i++)
	{
		ans[i] = 1;
		for (int j = 1; j <= n; j++)if (loc[i] > loc[j])ans[i]++;
	}
	for (int i = 1; i <= n; i++)
	{
		if (t < l - loc[i])loc[i] += t;
		else
		{
			temp = t;
			t -= l - loc[i];
			if (t < l)loc[i] = l - t;
			else loc[i] = t - l;
			t = temp;
		}
	}
	sort(loc + 1, loc + n + 1);
	for (int i = 1; i <= n; i++)cout << loc[ans[i]] << ((i < n) ? ' ' : '\n');
	return 0;
}

CCF-CSP-2018-3-3 URL映射

题目:

问题描述

  URL 映射是诸如 Django、Ruby on Rails 等网页框架 (web frameworks) 的一个重要组件。对于从浏览器发来的 HTTP 请求,URL 映射模块会解析请求中的 URL 地址,并将其分派给相应的处理代码。现在,请你来实现一个简单的 URL 映射功能。
  本题中 URL 映射功能的配置由若干条 URL 映射规则组成。当一个请求到达时,URL 映射功能会将请求中的 URL 地址按照配置的先后顺序逐一与这些规则进行匹配。当遇到第一条完全匹配的规则时,匹配成功,得到匹配的规则以及匹配的参数。若不能匹配任何一条规则,则匹配失败。
  本题输入的 URL 地址是以斜杠 / 作为分隔符的路径,保证以斜杠开头。其他合法字符还包括大小写英文字母、阿拉伯数字、减号 -、下划线 _ 和小数点 .。例如,/person/123/ 是一个合法的 URL 地址,而 /person/123? 则不合法(存在不合法的字符问号 ?)。另外,英文字母区分大小写,因此 /case/ 和 /CAse/ 是不同的 URL 地址。
  对于 URL 映射规则,同样是以斜杠开始。除了可以是正常的 URL 地址外,还可以包含参数,有以下 3 种:
  字符串 :用于匹配一段字符串,注意字符串里不能包含斜杠。例如,abcde0123。
  整数 :用于匹配一个不带符号的整数,全部由阿拉伯数字组成。例如,01234。
  路径 :用于匹配一段字符串,字符串可以包含斜杠。例如,abcd/0123/。
  以上 3 种参数都必须匹配非空的字符串。简便起见,题目规定规则中 前面一定是斜杠,后面要么是斜杠,要么是规则的结束(也就是该参数是规则的最后一部分)。而 的前面一定是斜杠,后面一定是规则的结束。无论是 URL 地址还是规则,都不会出现连续的斜杠。

输入格式

  输入第一行是两个正整数 n 和 m,分别表示 URL 映射的规则条数和待处理的 URL 地址个数,中间用一个空格字符分隔。
  第 2 行至第 n+1 行按匹配的先后顺序描述 URL 映射规则的配置信息。第 i+1 行包含两个字符串 pi 和 ri,其中 pi 表示 URL 匹配的规则,ri 表示这条 URL 匹配的名字。两个字符串都非空,且不包含空格字符,两者中间用一个空格字符分隔。
  第 n+2 行至第 n+m+1 行描述待处理的 URL 地址。第 n+1+i 行包含一个字符串 qi,表示待处理的 URL 地址,字符串中不包含空格字符。

输出格式

  输入共 m 行,第 i 行表示 qi 的匹配结果。如果匹配成功,设匹配了规则 pj ,则输出对应的 rj。同时,如果规则中有参数,则在同一行内依次输出匹配后的参数。注意整数参数输出时要把前导零去掉。相邻两项之间用一个空格字符分隔。如果匹配失败,则输出 404。

样例输入

5 4
/articles/2003/ special_case_2003
/articles// year_archive
/articles/// month_archive
/articles//// article_detail
/static/ static_serve
/articles/2004/
/articles/1985/09/aloha/
/articles/hello/
/static/js/jquery.js

样例输出

year_archive 2004
article_detail 1985 9 aloha
404
static_serve js/jquery.js

样例说明

  对于第 1 个地址 /articles/2004/,无法匹配第 1 条规则,可以匹配第 2 条规则,参数为 2004。
  对于第 2 个地址 /articles/1985/09/aloha/,只能匹配第 4 条规则,参数依次为 1985、9(已经去掉前导零)和 aloha。
  对于第 3 个地址 /articles/hello/,无法匹配任何一条规则。
  对于第 4 个地址 /static/js/jquery.js,可以匹配最后一条规则,参数为 js/jquery.js。

数据规模和约定

  1 ≤ n ≤ 100,1 ≤ m ≤ 100。
  所有输入行的长度不超过 100 个字符(不包含换行符)。
  保证输入的规则都是合法的。

代码:

#include
#include
using namespace std;
 
bool f(string s1, string s2,bool flag)
{
	int k1 = 0, k2 = 0, len1 = s1.length(), len2 = s2.length();
	while (k1 < len1 && k2 < len2)
	{
		if (s1[k1] == s2[k2])
		{
			k1++, k2++;
			continue;
		}
		if (s1[k1++] != '<')return false;
		if (flag)cout << ' ';
		if (s1[k1] == 'i')
		{
			bool flag2 = false;
			while (s2[k2] && s2[k2] >= '0' && s2[k2] <= '9')
			{
				if (s2[k2] > '0')flag2 = true;
				if (flag2 && flag)cout << s2[k2];
				k2++;
			}
			if (!flag2)return false;
			k1 += 4;
			continue;
		}
		if (s1[k1] == 's')
		{
			bool flag2 = false;
			while (s2[k2] && s2[k2] != '/')
			{
				flag2 = true;
				if (flag)cout << s2[k2];
				k2++;
			}
			if (!flag2)return false;
			k1 += 4;
			continue;
		}
		if (s1[k1] == 'p')
		{
			if (flag)while (s2[k2])cout << s2[k2++];
			return true;
		}
	}
	return (k1 == len1 && k2 == len2);
}
 
int main()
{
	int n, m;
	cin >> n >> m;
	string sn[100], name[100], s;
	for (int i = 0; i < n; i++)cin >> sn[i] >> name[i];
	for (int i = 0; i < m; i++)
	{
		cin >> s;
		bool flag = true;
		for (int i = 0; i < n && flag; i++)if (f(sn[i], s,false))
		{
			flag = false;
			cout << name[i];
			f(sn[i], s, true);
		}
		if (flag)cout << "404";
		cout << endl;
	}
	return 0;
}

热心网友给出了输入用例,当匹配的是0时本程序会匹配失败,这是一个bug

输入用例:

1 1
// aa
/0/

CCF-CSP-2018-3-4 棋局评估

题目:

  Alice和Bob正在玩井字棋游戏。
  井字棋游戏的规则很简单:两人轮流往3*3的棋盘中放棋子,Alice放的是“X”,Bob放的是“O”,Alice执先。当同一种棋子占据一行、一列或一条对角线的三个格子时,游戏结束,该种棋子的持有者获胜。当棋盘被填满的时候,游戏结束,双方平手。
  Alice设计了一种对棋局评分的方法:
  - 对于Alice已经获胜的局面,评估得分为(棋盘上的空格子数+1);
  - 对于Bob已经获胜的局面,评估得分为 -(棋盘上的空格子数+1);
  - 对于平局的局面,评估得分为0;


  例如上图中的局面,Alice已经获胜,同时棋盘上有2个空格,所以局面得分为2+1=3。
  由于Alice并不喜欢计算,所以他请教擅长编程的你,如果两人都以最优策略行棋,那么当前局面的最终得分会是多少?

输入格式

  输入的第一行包含一个正整数 T,表示数据的组数。
  每组数据输入有3行,每行有3个整数,用空格分隔,分别表示棋盘每个格子的状态。0表示格子为空,1表示格子中为“X”,2表示格子中为“O”。保证不会出现其他状态。
  保证输入的局面合法。(即保证输入的局面可以通过行棋到达,且保证没有双方同时获胜的情况)
  保证输入的局面轮到Alice行棋。

输出格式

  对于每组数据,输出一行一个整数,表示当前局面的得分。

样例输入

3
1 2 1
2 1 2
0 0 0
2 1 1
0 2 1
0 0 2
0 0 0
0 0 0
0 0 0

样例输出

3
-4
0

样例说明

  第一组数据:
  Alice将棋子放在左下角(或右下角)后,可以到达问题描述中的局面,得分为3。
  3为Alice行棋后能到达的局面中得分的最大值。
  第二组数据:


  Bob已经获胜(如图),此局面得分为-(3+1)=-4。
  第三组数据:
  井字棋中若双方都采用最优策略,游戏平局,最终得分为0。

数据规模和约定

  对于所有评测用例,1 ≤  T ≤ 5。

代码:

#include
#include
using namespace std;
 
int a[9],ans[3][3][3][3][3][3][3][3][3];
 
int isend()
{
	for (int i = 0; i < 3; i++)if (a[i] == a[i + 3] && a[i] == a[i + 6] && a[i])return a[i];
	for (int i = 0; i < 9; i += 3)if (a[i] == a[i + 1] && a[i] == a[i + 2] && a[i])return a[i];
	if (a[0] == a[4] && a[4] == a[8] && a[4])return a[4];
	if (a[2] == a[4] && a[4] == a[6] && a[4])return a[4];
	return 0;
}
 
int dp()
{
	if (ans[a[0]][a[1]][a[2]][a[3]][a[4]][a[5]][a[6]][a[7]][a[8]]< 100)
		return ans[a[0]][a[1]][a[2]][a[3]][a[4]][a[5]][a[6]][a[7]][a[8]];
	int s = 0;
	for (int i = 0; i < 9; i++)if (a[i])s++;
	if (isend() == 1)return 10 - s;
	if (isend() == 2)return s - 10;
	if (s == 9)return 0;
	int r;
	if (s % 2)
	{
		r = 100;
		for (int i = 0; i < 9; i++)
		{
			if (a[i])continue;
			a[i] = 2;
			if (r > dp())r = dp();
			a[i] = 0;
		}
	}
	else
	{
		r = -100;
		for (int i = 0; i < 9; i++)
		{
			if (a[i])continue;
			a[i] = 1;
			if (r < dp())r = dp();
			a[i] = 0;
		}
	}
	ans[a[0]][a[1]][a[2]][a[3]][a[4]][a[5]][a[6]][a[7]][a[8]] = r;
	return r;
}
 
int main()
{
	int n;
	cin >> n;
	for (int i = 0; i < n; i++)
	{
		memset(ans, 1, sizeof(ans));
		for (int i = 0; i < 9; i++)cin >> a[i];
		cout << dp() << endl;
	}
	return 0;
}

 

你可能感兴趣的:(CCF-CSP 2016-2018)