CCF-CSP 2013年12月 题解

201312-1 出现次数最多的数

题目:

问题描述
试题编号: 201312-1
试题名称: 出现次数最多的数
时间限制: 1.0s
内存限制: 256.0MB
问题描述
  给定n个正整数,找出它们中出现次数最多的数。如果这样的数有多个,请输出其中最小的一个。
输入格式
  输入的第一行只有一个正整数n(1 ≤ n ≤ 1000),表示数字的个数。
  输入的第二行有n个整数s1, s2, …, sn (1 ≤ si ≤ 10000, 1 ≤ i ≤ n)。相邻的数用空格分隔。
输出格式
  输出这n个次数中出现次数最多的数。如果这样的数有多个,输出其中最小的一个。
样例输入
6
10 1 10 20 30 20
样例输出
10

解题思路:

使用pair类型的数组 A 来存储数字 x 以及 x 出现的次数,利用sort对数字出现次数进行排序后,再对最大的出现次数值范围内的数字大小进行排序,最后输出A[0]即可。

代码:

#include<bits/stdc++.h>
using namespace std;
pair<int, int > A[10010];
int n;

bool cmp1(pair<int, int> a, pair<int, int> b) {
	return a.first < b.first;
}

bool cmp2(pair<int, int> a, pair<int, int> b) {
	return a.second > b.second;
}

int main() {
	cin >> n;
	for (int i = 0; i < n; i++) {
		int tmp;
		cin >> tmp;
		A[tmp].first = tmp;
		A[tmp].second++;
	}
	sort(A, A + 10010, cmp2);
	int cnt = 0, tmp = A[0].second;
	while (tmp == A[cnt++].second);
	sort(A, A + cnt - 1, cmp1);
	cout << A[0].first << endl;

	return 0;
}

201312-2 ISBN号码

题目:

试题编号: 201312-2
试题名称: ISBN号码
时间限制: 1.0s
内存限制: 256.0MB
问题描述
  每一本正式出版的图书都有一个ISBN号码与之对应,ISBN码包括9位数字、1位识别码和3位分隔符,其规定格式如“x-xxx-xxxxx-x”,其中符号“-”是分隔符(键盘上的减号),最后一位是识别码,例如0-670-82162-4就是一个标准的ISBN码。ISBN码的首位数字表示书籍的出版语言,例如0代表英语;第一个分隔符“-”之后的三位数字代表出版社,例如670代表维京出版社;第二个分隔之后的五位数字代表该书在出版社的编号;最后一位为识别码。
  识别码的计算方法如下:
  首位数字乘以1加上次位数字乘以2……以此类推,用所得的结果mod 11,所得的余数即为识别码,如果余数为10,则识别码为大写字母X。例如ISBN号码0-670-82162-4中的识别码4是这样得到的:对067082162这9个数字,从左至右,分别乘以1,2,…,9,再求和,即0×1+6×2+……+2×9=158,然后取158 mod 11的结果4作为识别码。
  编写程序判断输入的ISBN号码中识别码是否正确,如果正确,则仅输出“Right”;如果错误,则输出是正确的ISBN号码。
输入格式
  输入只有一行,是一个字符序列,表示一本书的ISBN号码(保证输入符合ISBN号码的格式要求)。
输出格式
  输出一行,假如输入的ISBN号码的识别码正确,那么输出“Right”,否则,按照规定的格式,输出正确的ISBN号码(包括分隔符“-”)。
样例输入
0-670-82162-4
样例输出
Right
样例输入
0-670-82162-0
样例输出
0-670-82162-4

解题思路:

对输入字符串的前9位数字进行相应的运算得到正确的识别码,再与最后一位数字比较即可。

代码:

#include<bits/stdc++.h>
using namespace std;

string a;

int main() {
	cin >> a;
	int ans = 0, b[20];
	int j = 0;

	for (int i = 0; i < 13; i++) {
		if (a[i] != '-') {
			b[j++] = a[i] - '0';
		}
	}
	for (int i = 0; i < 9; i++) {
		ans += (i + 1) * b[i];
	}
	ans %= 11;

	//获得识别码对应的字符
	if (ans == 10) ans = 'X';
	else ans += '0';

	//做比较 输出
	if (ans == a[12]) cout << "Right" << endl;
	else {
		a[12] = ans;
		cout << a << endl;
	}

	return 0;
}

201312-3 最大的矩形

题目:

试题编号: 201312-3
试题名称: 最大的矩形
时间限制: 1.0s
内存限制: 256.0MB
问题描述:
问题描述
  在横轴上放了n个相邻的矩形,每个矩形的宽度是1,而第i(1 ≤ i ≤ n)个矩形的高度是hi。这n个矩形构成了一个直方图。例如,下图中六个矩形的高度就分别是3, 1, 6, 5, 2, 3。

在这里插入图片描述

请找出能放在给定直方图里面积最大的矩形,它的边要与坐标轴平行。对于上面给出的例子,最大矩形如下图所示的阴影部分,面积是10。
在这里插入图片描述

输入格式
  第一行包含一个整数n,即矩形的数量(1 ≤ n ≤ 1000)。
  第二行包含n 个整数h1, h2, … , hn,相邻的数之间由空格分隔。(1 ≤ hi ≤ 10000)。hi是第i个矩形的高度。
输出格式
  输出一行,包含一个整数,即给定直方图内的最大矩形的面积。
样例输入
6
3 1 6 5 2 3
样例输出
10

解题思路

方法一:(暴力破解) O(n^2)
对每一个矩形从此矩形开始分别向左和向右扫描识别有多少个连续的矩形的高度大于或等于该矩形,而后得到以该矩形为高度得到的最大面积,用该面积与最大值进行比较。完成一遍扫描后得到整个直方图中的最大面积。

**方法二:单调栈 ** O(n)
对于直方图中的一个矩形,求以它为高的最大面积,必然是碰到一个高度小于它自身的就碰到了一个边界。那么可以考虑对直方图从左向右扫描,构造出一个高度递增的单调栈。操作过程如下:
如果栈为空,那么直接将当前扫描的矩形 Cur 压入栈中;
如果当前扫描的矩形 Cur 得到高度大于栈顶 Top 的高度,那么直接将 Cur 压入栈中;
如果 Cur 小于 Top ,那么将 Top 弹出,Cur 再与新的 Top 进行比较,直到 Cur >= Top 。
形成得到的单调栈的一些特点:

  1. 单调栈中任意两个连续的矩形A、B(B>=A)之间的矩形高度均大于B,因为如果存在A<C<B那么C一定在A、B之间;
  2. 如果单调栈只剩一个矩形,那么这个矩形的高度是至此最小的,否则其前面应该还会有矩形,不会以该矩形为最后一个栈元素;
    根据以上特点可以进行如下操作来求得最大的矩形面积,每次从栈中弹出一个矩形,那么对这个矩形求以它为高的最大面积。由于 特点1 ,假设当前与栈顶比较的矩形下标为 i ,未弹出栈时栈顶表示矩形下标 top_Pre ,由于H[top_Pre] > H[i] ,所以将top_Pre弹出栈,新的栈顶下标为 top_Cur 。由于top_Pre和top_Cur之间的矩形高度都大于 H[top_Pre],所以以top_Pre为高度的最大矩形面积为:
    ** (i - top_Cur - 1) * H[top_Pre] **
    当弹出栈顶后栈为空,那么由于 特点2 ,以 top_Pre 为高度的矩形面积为:
    ** H[top_Pre] * i **

代码:

暴力破解:

#include<bits/stdc++.h>

using namespace std;
int H[2010];
int n;
/*暴力破解
:因为n<=1000,所以O(n^2)的计算次数为 1e6 < 1e8
暴力破解是可行的
*/
int main() {
	cin >> n;
	for (int i = 0; i < n; i++)
		cin >> H[i];
	int Max = 0;
	int S = 0;
	for (int i = 0; i < n; i++) {
		int L = 0, R = 0;
		for (int j = i; j >= 0; j--) {  //从i开始向左扫描寻找 左部宽度
			if (H[j] >= H[i]) L++;
			else break;
		}
		for (int j = i; j < n; j++) {
			if (H[j] >= H[i]) R++;
			else break;
		}
		int W = L + R - 1;
		S = H[i] * W;
		if (S > Max) Max = S;
	}
	cout << Max << endl;

	return 0;
}

单调栈

#include<bits/stdc++.h>

using namespace std;
int H[2010];
int n;
//   单调栈方法
int main() {
	cin >> n;
	for (int i = 0; i < n; i++) {
		cin >> H[i];
	}
	H[n] = 0;
	stack<int> S;  //用一个栈来存储
	long long Max = 0;
	for (int i = 0; i <= n; i++) {  //一定要添加 i=n时候的情况 因为最后还需要对剩余的单调栈进行判断
		if (S.empty() || H[i] >= H[S.top()]) {   //如果新来的值大于栈顶值 或者 栈为空 直接压入即可
			S.push(i);
		}
		else {
			while (H[i] < H[S.top()]) {  //如果栈不空 且 栈顶值大于H[i]
				int top = S.top();
				S.pop();  //将栈顶弹出是因为需要知道下一个栈顶的值
				if (!S.empty()) {
					Max = max(Max, (long long)H[top] * (i - S.top() - 1));   //在第一个栈顶和第二个栈顶之间的所有值一定大于第一个栈顶 否则两栈顶之间要添加其他的值
				}
				else{
					Max = max(Max, (long long)H[top] * i);  //栈空说明从H[0] 到 H[i-1]中最小的值就是弹出的栈顶
					break;
				}
			}
			S.push(i);
		}
	}
	
	cout << Max << endl;

	return 0;
}

201312-4 有趣的数

题目:

试题编号: 201312-4
试题名称: 有趣的数
时间限制: 1.0s
内存限制: 256.0MB
问题描述
  我们把一个数称为有趣的,当且仅当:
  1. 它的数字只包含0, 1, 2, 3,且这四个数字都出现过至少一次。
  2. 所有的0都出现在所有的1之前,而所有的2都出现在所有的3之前。
  3. 最高位数字不为0。
  因此,符合我们定义的最小的有趣的数是2013。除此以外,4位的有趣的数还有两个:2031和2301。
  请计算恰好有n位的有趣的数的个数。由于答案可能非常大,只需要输出答案除以1000000007的余数。
输入格式
  输入只有一行,包括恰好一个正整数n (4 ≤ n ≤ 1000)。
输出格式
  输出只有一行,包括恰好n 位的整数中有趣的数的个数除以1000000007的余数。
样例输入
4
样例输出
3

解题思路:

一共四种数字 0必须在1前 2必须在3前
仅有以下几种情况:
(1) 只填写了2 只有数字 2
(2) 在(1)的基础上填写了0 只有数字 2 和 0
在自己的基础上填写2或者0
(3) 在(1)的基础上填写了3 只有数字 2 和 3
在自己的基础上填写3
(4) 在(2)的基础上填写了1 只有数字 2 、 0 、1
在自己的基础上填写1或者2
(5) 在(2)的基础上填写了3 只有数字 2 、 0 、3
在(3)的基础上填写了0
在自己的基础上填写3或者0
(6) 在(4)的基础上填写了3 1 、2 、 3、 0全部都有
在(5)的基础上填写了1
在自己的基础上填写1或者3

代码:

#include<bits/stdc++.h>

using namespace std;
#define ll long long
ll dp[1010][7];
#define Mod 1000000007 

int main() {
	ll n;
	cin >> n;

	for (ll i = 1; i <= n; i++) {
		dp[i][1] = 1;
		dp[i][2] = (dp[i - 1][1] + dp[i - 1][2] * 2) % Mod;
		dp[i][3] = (dp[i - 1][1] + dp[i - 1][3]) % Mod;
		dp[i][4] = (dp[i - 1][2] + dp[i - 1][4] * 2) % Mod;
		dp[i][5] = (dp[i - 1][2] + dp[i - 1][3] + dp[i - 1][5] * 2) % Mod;
		dp[i][6] = (dp[i - 1][4] + dp[i - 1][5] + dp[i - 1][6] * 2) % Mod;
	}
	cout << dp[n][6] << endl;

	return 0;
}

201312-5 I’m stuck!

题目:

试题编号: 201312-5
试题名称: I’m stuck!
时间限制: 1.0s
内存限制: 256.0MB
问题描述
  给定一个R行C列的地图,地图的每一个方格可能是’#’, ‘+’, ‘-’, ‘|’, ‘.’, ‘S’, ‘T’七个字符中的一个,分别表示如下意思:
  ‘#’: 任何时候玩家都不能移动到此方格;
  ‘+’: 当玩家到达这一方格后,下一步可以向上下左右四个方向相邻的任意一个非’#‘方格移动一格;
  ‘-’: 当玩家到达这一方格后,下一步可以向左右两个方向相邻的一个非’#‘方格移动一格;
  ‘|’: 当玩家到达这一方格后,下一步可以向上下两个方向相邻的一个非’#‘方格移动一格;
  ‘.’: 当玩家到达这一方格后,下一步只能向下移动一格。如果下面相邻的方格为’#’,则玩家不能再移动;
  ‘S’: 玩家的初始位置,地图中只会有一个初始位置。玩家到达这一方格后,下一步可以向上下左右四个方向相邻的任意一个非’#‘方格移动一格;
  ‘T’: 玩家的目标位置,地图中只会有一个目标位置。玩家到达这一方格后,可以选择完成任务,也可以选择不完成任务继续移动。如果继续移动下一步可以向上下左右四个方向相邻的任意一个非’#‘方格移动一格。
  
  此外,玩家不能移动出地图。
  请找出满足下面两个性质的方格个数:
  1. 玩家可以从初始位置移动到此方格;
  2. 玩家不可以从此方格移动到目标位置。
  
输入格式
  输入的第一行包括两个整数R 和C,分别表示地图的行和列数。(1 ≤ R, C ≤ 50)。
  接下来的R行每行都包含C个字符。它们表示地图的格子。地图上恰好有一个’S’和一个’T’。
输出格式
  如果玩家在初始位置就已经不能到达终点了,就输出“I’m stuck!”(不含双引号)。否则的话,输出满足性质的方格的个数。
样例输入
5 5
–±+
…|#.
…|##
S-±T
####.
样例输出
2
样例说明
  如果把满足性质的方格在地图上用’X’标记出来的话,地图如下所示:
  --±+
  …|#X
  …|##
  S-±T
  ####X

解题思路:

使用bfs算法。第一次bfs记录所有能够起点 'S' 到达点,第二次bfs对这些点进行判断是否能够到达终点 'T' 。

代码

/*
使用一次bfs得到从起点能够到达的点 在使用一次bfs判断从第一次bfs得到的点能否到达终点
只有90分 希望写第二遍能有100分
*/
#include<bits/stdc++.h>
using namespace std;
pair<int, int> Start, Final;  //起始点和终点
char Map[60][60];  //记录地图信息
int vis1[60][60], vis2[60][60];  //vis[i][j]表示是否能从起点到达(i,j)
vector<pair<int, int> > direction[4]; //表示 '+' '|' '-' '.'的前进方向
int R, C;

bool border(int x, int y) { //判断是否为边界条件
	return x<1 || y<1 || x>R || y>C || Map[x][y] == '#';  //true表示在边界
}

int value(int x, int y) {
	char tmp = Map[x][y];
	int v = 0;
	switch (tmp) {
		case '+':v = 0; break;
		case '|':v = 1; break;
		case '-':v = 2; break;
		case '.':v = 3; break;
	}
	return v;
}

void bfs1(pair<int, int> S) {  //从源点出发能到达的地点  最后得到vis1数组结果
	queue<pair<int, int> > q;
	q.push(S);
	while (!q.empty()) {
		pair<int, int> tmp;
		tmp = q.front(); q.pop();
		int tx = tmp.first, ty = tmp.second;
		if (border(tx, ty)) continue;
		if (vis1[tx][ty]) continue;
		vis1[tx][ty] = 1;
		int v = value(tx, ty);
		for (int i = 0; i < direction[v].size(); i++) {
			pair<int, int> next = direction[v][i];  //direction数组只是方向的表示
			int nx = tx + next.first, ny = ty + next.second;
			if (vis1[nx][ny] || border(nx, ny)) continue;
			q.push(pair<int, int>{nx, ny});
		}
	}
}

bool bfs2(int x, int y) {  //判断从 (x,y)能否到达 T 点
	queue<pair<int, int> > q;
	q.push(pair<int, int>{x, y});
	memset(vis2, 0, sizeof(vis2));
	while (!q.empty()) {
		pair<int, int> tmp;
		tmp = q.front(); q.pop();
		int tx = tmp.first, ty = tmp.second;
		if (tx == Final.first && ty == Final.second) return false;  //到达了目标点
		if (border(tx, ty)) continue;
		if (vis2[tx][ty]) continue;
		vis2[tx][ty] = 1;
		int v = value(tx, ty);
		for (int i = 0; i < direction[v].size(); i++) {
			pair<int, int> next = direction[v][i];  //direction数组只是方向的表示
			int nx = tx + next.first, ny = ty + next.second;
			if (vis2[nx][ny]) continue;
			q.push(pair<int, int>{nx, ny});
		}
	}
	return true;
}

int main() {
	cin >> R >> C;
	direction[0].push_back(pair<int, int>{-1, 0}); direction[0].push_back(pair<int, int>{1, 0}); direction[0].push_back(pair<int, int>{0, -1});
	direction[0].push_back(pair<int, int>{0, 1}); direction[1].push_back(pair<int, int>{-1, 0}); direction[1].push_back(pair<int, int>{1, 0});
	direction[2].push_back(pair<int, int>{0, -1}); direction[2].push_back(pair<int, int>{0, 1}); direction[3].push_back(pair<int, int>{1, 0});
	for (int i = 1; i <= R; i++) {
		for (int j = 1; j <= C; j++) {
			cin >> Map[i][j];
			if (Map[i][j] == 'S') {
				Map[i][j] = '+';
				Start.first = i; Start.second = j;
			}
			if (Map[i][j] == 'T') {
				Map[i][j] = '+';
				Final.first = i; Final.second = j;
			}
		}
	}
	int cnt = 0;
	bfs1(Start);
	if (!vis1[Final.first][Final.second]) {
		cout << "I’m stuck!" << endl;
		return 0;
	}
	for(int i=1;i<=R;i++)
		for (int j = 1; j <= C; j++) {
			if (vis1[i][j])
				if (bfs2(i, j)) cnt++;
		}
	cout << cnt << endl;
	return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值