蓝桥杯算法训练合集一 1.印章2.拿金币3.数字游戏4.无聊的逗5.礼物

目录

1.印章(动态规划)

2.拿金币(动态规划)

3.数字游戏(搜索)

4.无聊的逗(状态搜索)

5.礼物(二分法和前缀和)


1.印章(动态规划)

问题描述

共有n种图案的印章,每种图案的出现概率相同。小A买了m张印章,求小A集齐n种印章的概率。

输入格式

一行两个正整数n和m

输出格式

一个实数P表示答案,保留4位小数。

样例输入

2 3

样例输出

0.7500

数据规模与约定

1≤n,m≤20

示例代码

#include 
#include
#include
using namespace std;
int main() {
	int n, m;
	float dp[21][21], p, temp = 0;//dp[i][j]为抽i个印章能凑齐j种印章的几率
	cin >> n >> m;
	p = float(1) / n;//只抽一个印章但抽到某一个特定印章的几率
	if (m < n) {//抽的印章数小于印章种类数,则这种几率为0
		cout << setprecision(4) << fixed << temp << endl;
		return 0;
	}
	dp[1][1] = 1;//只抽一个印章肯定能凑齐一种印章
	for (int i = 2; i <= m; i++) {//抽i个印章凑齐1种印章的几率,保证后面的i-1个印章抽的和第一个一样
		dp[i][1] = dp[i - 1][1] * p;
	}
	for (int j = 2; j <= m; j++) {
		for (int i = 2; i <= j; i++) {//抽m个印章凑齐i种印章的几率
			if (j == i) {//如果要抽的印章数和凑齐的印章数相等,则抽到的每一个印章都和之前的不一样
				dp[j][i] = dp[j - 1][i - 1] * ((n + 1 - i)) * p;
			}
			else {//否则可能是 抽到的最后一个印章和之前抽到的任意一个印章相同 以及 抽到的最后一个印章和之前的任意一个印章都不相同
				dp[j][i] = dp[j - 1][i] * i * p + dp[j - 1][i - 1] * (n + 1 - i) * p;
			}
		}
	}
	cout << setprecision(4) << fixed << dp[m][n] << endl;
	return 0;
}

2.拿金币(动态规划)

问题描述

有一个N x N的方格,每一个格子都有一些金币,只要站在格子里就能拿到里面的金币。你站在最左上角的格子里,每次可以从一个格子走到它右边或下边的格子里。请问如何走才能拿到最多的金币。

输入格式

第一行输入一个正整数n。
以下n行描述该方格。金币数保证是不超过1000的正整数。

输出格式

最多能拿金币数量。

样例输入

3
1 3 3
2 2 2
3 1 2

样例输出

11

数据规模与约定

n<=1000

示例代码

#include 
#include
#include
int dp[1001][1001];
using namespace std;
int main() {
	int n;
	cin >> n;
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= n; j++) {
			cin >> dp[i][j];
		}
	}
	for (int i = 2; i <= n; i++) {//对第一行进行处理,只能向右走
		dp[i][1] += dp[i - 1][1];
	}
	for (int i = 2; i <= n; i++) {//对第一列进行处理,只能向下走
		dp[1][i] += dp[1][i - 1];
	}
	for (int i = 2; i <= n; i++) {
		for (int j = 2; j <= n; j++) {
			dp[i][j] += max(dp[i - 1][j], dp[i][j - 1]);//选取向下和向右走的最大值,这样局部的dp[i][j]就是最大值
		}
	}
	cout << dp[n][n];
	return 0;
}

3.数字游戏(搜索)

问题描述

给定一个1~N的排列a[i],每次将相邻两个数相加,得到新序列,再对新序列重复这样的操作,显然每次得到的序列都比上一次的序列长度少1,最终只剩一个数字。
例如:
3 1 2 4
4 3 6
7 9
16
现在如果知道N和最后得到的数字sum,请求出最初序列a[i],为1~N的一个排列。若有多种答案,则输出字典序最小的那一个。数据保证有解。

输入格式

第1行为两个正整数n,sum

输出格式

一个1~N的一个排列

样例输入

4 16

样例输出

3 1 2 4

数据规模与约定

0

示例代码

#include 
#include
using namespace std;
int main() {
	int n, a[11][11], b[11], sum, sum1 = 0, count = 1, count1 = 1;
	cin >> n;
	cin >> sum;
	for (int i = n; i >= 1; i--) {
		count1 *= i;
	}
	for (int i = 1; i <= n; i++) {
		b[i] = i;
	}
	if (n == 1) {//求每一项的系数,类似于(a+b)的n次方的各项系数,和杨辉三角形很像
		a[1][1] = 1;
	}
	/*
	  n 各项系数
	  1 1
	  2 1 1
	  3 1 2 1
	  4 1 3 3 1
	  5 1 4 6 4 1
	  ...............
	 */
	else if (n == 2) {
		a[2][1] = 1;
		a[2][2] = 1;
	}
	else {
		for (int i = 2; i <= n; i++) {
			for (int j = 1; j <= i; j++) {
				if (j == 1 || j == i) {
					a[i][j] = 1;
				}
				else {
					a[i][j] = a[i - 1][j - 1] + a[i - 1][j];
				}
			}
		}
	}
	while (sum1 != sum) {//当还未找到需要的数时
		sum1 = 0;//sum1为此次1-n数字的排序的相邻总和
		for (int i = 1; i <= n; i++) {
			sum1 += a[n][i] * b[i];
		}
		count++;
		if (count == count1) {//如果1-n之间的数字排序排完了还是没有找到,说明不存在对应的结果
			return 0;
		}
		next_permutation(b + 1, b + n + 1);//将1-n组成的数进行从小到大的排序
	}
	prev_permutation(b + 1, b + n + 1);//多做了一次next则要prev降序一次
	for (int i = 1; i <= n; i++) {
		cout << b[i] << " ";
	}
	return 0;
}

4.无聊的逗(状态搜索)

问题描述

逗志芃在干了很多事情后终于闲下来了,然后就陷入了深深的无聊中。不过他想到了一个游戏来使他更无聊。他拿出n个木棍,然后选出其中一些粘成一根长的,然后再选一些粘成另一个长的,他想知道在两根一样长的情况下长度最长是多少。

输入格式

第一行一个数n,表示n个棍子。第二行n个数,每个数表示一根棍子的长度。

输出格式

一个数,最大的长度。

样例输入

4
1 2 3 1

样例输出

3

数据规模与约定

n<=15

示例代码

#include
using namespace std;
int a[15];
int b[1 << 15] = { 0 };
int max(int a, int b) {
	if (a > b) {
		return a;
	}
	else {
		return b;
	}
}
int main() {
	int n, ans = 0;
	cin >> n;
	for (int i = 0; i < n; i++) {
		cin >> a[i];
	}
	for (int i = 0; i < (1 << n); i++) {//按照二进制来表示木棍选取情况 如1100表示选取的是第四根和第三根木棍,共2的n次方种选取情况
		for (int j = 0; j < n; j++) {
			if (i & (1 << j)) {//如果i放置的木棍中有第j根木棍  用与是因为二进制
				b[i] += a[j];
			}
		}
	}
	for (int i = 0; i < (1 << n); i++) {//先选取的木棍
		for (int j = 0; j < (1 << n); j++) {//后选取的木棍
			if (!(i & j) && b[i] == b[j]) {//如果不是同一种选取方式而且长度相同
				ans = max(ans, b[i]);
			}
		}
	}
	cout << ans;
}

5.礼物(二分法和前缀和)

问题描述

JiaoShou在爱琳大陆的旅行完毕,即将回家,为了纪念这次旅行,他决定带回一些礼物给好朋友。
在走出了怪物森林以后,JiaoShou看到了排成一排的N个石子。
这些石子很漂亮,JiaoShou决定以此为礼物。
但是这N个石子被施加了一种特殊的魔法。
如果要取走石子,必须按照以下的规则去取。
每次必须取连续的2*K个石子,并且满足前K个石子的重量和小于等于S,后K个石子的重量和小于等于S。
由于时间紧迫,Jiaoshou只能取一次。
现在JiaoShou找到了聪明的你,问他最多可以带走多少个石子。

输入描述

第一行两个整数N、S。
第二行N个整数,用空格隔开,表示每个石子的重量。

输出格式

第一行输出一个数表示JiaoShou最多能取走多少个石子。

样例输入

8 3
1 1 1 1 1 1 1 1

样例输出

6

样例解释

任意选择连续的6个1即可。

数据规模与约定

对于20%的数据:N<=1000
对于70%的数据:N<=100,000
对于100%的数据:N<=1000,000,S<=10^12,每个石子的重量小于等于10^9,且非负

示例代码

#include
#include
using namespace std;
int n;
long long s;
long long a[1000001], b[1000002] = { 0 };
bool inject(int c) {
	for (int i = c; i <= n - c; i++) {//i=c保证找到向左能有连续c个长度的石头,i<=n-c保证向右有连续c个长度的石头
		if (b[i] - b[i-c] <= s && b[c + i] - b[i] <= s) {//如果i左边的连续c个和右边连续c个石头的重量和都小于等于s
			return true;
		}
	}
	return false;//如果这个长度下都不符合
}
int main() {
	cin >> n >> s;
	for (int i = 1; i <= n; i++) {
		cin >> a[i];
		b[i] = b[i - 1] + a[i];//前缀和 b[n]-b[n-i]为 a[n-i+1]到a[n]之间的连续的和
	}
	int l = 1;
	int r = n;
	int k;
	while (l < r) {//当未找到时,l> 1;//二分法,取一半,(l+r)/2会造成死循环。。。。。。。。
		if (inject(k)) {//这个长度符合要求,可能有更大的长度
			l = k;
		}
		else {//这个长度不符合要求,长度要更小一些
			r = k - 1;
		}
	}
	cout <<2* l;
}

你可能感兴趣的:(蓝桥杯,游戏)