目录
1A:啤酒和饮料(填空)(枚举)
2B:切面条(填空)
3C:李白打酒(填空)(dfs)
4D:史丰收速算(代码填空)
5E:打印图形(代码填空)
6F:奇怪的分式(填空)
7G:六角填数(填空)
8H:蚂蚁感冒(编程)
解析代码
9I:地宫取宝(编程)
解析代码
10J:小朋友排队(编程)
解析代码
1A:啤酒和饮料(填空)(枚举)
题目描述:
啤酒每罐2.3元,饮料每罐1.9元。小明买了若干啤酒和饮料,一共花了82.3元。我们还知道他买的啤酒比饮料的数量少,请你计算他买了几罐啤酒。
题目分析:简单的循环暴力,两层循环就可。
答案11
#include <iostream>
using namespace std;
int main()
{
int sum = 0;
for (int i = 0; i < 100; ++i)
{
for (int j = i; j < 100; ++j)
{
if (82.3 - 2.3 * i == 1.9 * j)
{
cout << i << " " << j;
return 0;
}
}
}
return 0;
}
2B:切面条(填空)
题目描述:
一根高筋拉面,中间切一刀,可以得到2根面条。
如果先对折1次,中间切一刀,可以得到3根面条。
如果连续对折2次,中间切一刀,可以得到5根面条。
那么,连续对折10次,中间切一刀,会得到多少面条呢?
题目解析:
按题目所给形式暴力模拟即可,答案1025
#include <iostream>
#include <cmath>
using namespace std;
int main()
{
//计算对折拐弯处数目(等比公式推导)
int a = pow(2, 10) - 1;
//无对折情况下数目
int b = pow(2, 11);
//最后面条数目
cout << b - a;
return 0;
}
3C:李白打酒(填空)(dfs)
答案14
暴力dfs代码:
#include<iostream>
using namespace std;
int res;
void dfs(int store, int flower, int wine)
{
if (store == 0 && flower == 0 && wine == 1)
res++;
if (store > 0)
dfs(store - 1, flower, wine * 2);
if (flower > 0)
dfs(store, flower - 1, wine - 1);
}
int main()
{
dfs(5, 9, 2);
cout << res;
return 0;
}
4D:史丰收速算(代码填空)
史丰收速算法的革命性贡献是:从高位算起,预测进位。不需要九九表,彻底颠覆了传统手算!
速算的核心基础是:1位数乘以多位数的乘法。
其中,乘以7是最复杂的,就以它为例。
因为,1/7 是个循环小数:0.142857…,如果多位数超过 142857…,就要进1
同理,2/7, 3/7, … 6/7 也都是类似的循环小数,多位数超过 n/7,就要进n
下面的程序模拟了史丰收速算法中乘以7的运算过程。
乘以 7 的个位规律是:偶数乘以2,奇数乘以2再加5,都只取个位。
乘以 7 的进位规律是:
满 142857… 进1,
满 285714… 进2,
满 428571… 进3,
满 571428… 进4,
满 714285… 进5,
满 857142… 进6
请分析程序流程,填写划线部分缺少的代码。//计算个位 int ge_wei(int a) { if(a % 2 == 0) return (a * 2) % 10; else return (a * 2 + 5) % 10; } //计算进位 int jin_wei(char* p) { char* level[] = { "142857", "285714", "428571", "571428", "714285", "857142" }; char buf[7]; buf[6] = '\0'; strncpy(buf,p,6); int i; for(i=5; i>=0; i--){ int r = strcmp(level[i], buf); if(r<0) return i+1; while(r==0){ p += 6; strncpy(buf,p,6); r = strcmp(level[i], buf); if(r<0) return i+1; ______________________________; //填空 } } return 0; } //多位数乘以7 void f(char* s) { int head = jin_wei(s); if(head > 0) printf("%d", head); char* p = s; while(*p){ int a = (*p-'0'); int x = (ge_wei(a) + jin_wei(p+1)) % 10; printf("%d",x); p++; } printf("\n"); } int main() { f("428571428571"); f("34553834937543"); return 0; }
题目解析:
函数名已经写得很清楚了:进位。既然有进那么就有不进位:答案为:
if(r>0) return i
5E:打印图形(代码填空)
题目描述:
小明在X星球的城堡中发现了如下图形和文字:
小明开动脑筋,编写了如下的程序,实现该图形的打印。
#define N 70
void f(char a[][N], int rank, int row, int col)
{
if(rank==1){
a[row][col] = '*';
return;
}
int w = 1;
int i;
for(i=0; i<rank-1; i++) w *= 2;
____________________________________________;
f(a, rank-1, row+w/2, col);
f(a, rank-1, row+w/2, col+w);
}
int main()
{
char a[N][N];
int i,j;
for(i=0;i<N;i++)
for(j=0;j<N;j++) a[i][j] = ' ';
f(a,6,0,0);
for(i=0; i<N; i++){
for(j=0; j<N; j++) printf("%c",a[i][j]);
printf("\n");
}
return 0;
}
请仔细分析程序逻辑,填写缺失代码部分。
题目解析:
这里使用递归的猜想,对于这里,我们要修改的就是递归函数里后面两个参数里的三个变量col,row,w,这里使用的方法是修改参数,然后通过结果发现,col控制列,col控制行,w控制空格数,所以最后调调答案就出来了,这种递归题在蓝桥杯中通过边调试边看结果最快。
所以三层递归就是这个意思:
f(a, rank - 1, row, col + w / 2); // 处理最上面的三角形
f(a, rank - 1, row + w / 2, col); // 处理左下角三角形
f(a, rank - 1, row + w / 2, col + w); // 处理右下角三角形
答案:
f(a, rank-1, row, col + w / 2)
6F:奇怪的分式(填空)
题目描述:
上小学的时候,小明经常自己发明新算法。一次,老师出的题目是:1/4 乘以 8/5 小明居然把分子拼接在一起,分母拼接在一起,答案是:18/45 (参见图1.png)老师刚想批评他,转念一想,这个答案凑巧也对啊,真是见鬼! 对于分子、分母都是 1~9 中的一位数的情况,还有哪些算式可以这样计算呢? 请写出所有不同算式的个数(包括题中举例的)。显然,交换分子分母后,例如:4/1 乘以 5/8 是满足要求的,这算做不同的算式。但对于分子分母相同的情况,2/2 乘以 3/3 这样的类型太多了,不在计数之列!
注意:答案是个整数(考虑对称性,肯定是偶数)。请通过浏览器提交。不要书写多余的内容。
题目分析:
对于此题用的方法就是暴力枚举,4层循环,需注意的是:
- 分子分母要用不同的数
- 整数相除用浮点数
- 浮点数比价大小用fabs函数
答案14(可以把abcd打印出来验证)
#include <iostream>
#include <cmath>
using namespace std;
int main()
{
int cnt = 0;
for (int a = 1; a < 10; a++)
{
for (int b = 1; b < 10; b++)
{
if (a == b) // 要满足分子分母不同
continue;
for (int c = 1; c < 10; c++)
{
for (int d = 1; d < 10; d++)
{
if (c == d)
continue;
if (fabs(a * c * 1.0 / (b * d) - (a * 10.0 + c) / (b * 10 + d)) < 1e-5)
++cnt; // 浮点数判断相等
}
}
}
}
cout << cnt << endl;
return 0;
}
7G:六角填数(填空)
题目描述:
如图【1.png】所示六角形中,填入1~12的数字。
使得每条直线上的数字之和都相同。
图中,已经替你填好了3个数字,请你计算星号位置所代表的数字是多少?
题目解析:
#include <iostream>
#include <algorithm>
using namespace std;
int num[9] = { 2,4,5,6,7,9,10,11,12 };
bool jude() // 判断六条直线是否相等
{
int ans1 = 1 + num[1] + num[4] + num[8];
int ans2 = 1 + num[0] + num[3] + num[5];
int ans3 = 8 + num[0] + num[1] + num[2];
int ans4 = 8 + num[3] + num[6] + 3;
int ans5 = 3 + num[7] + num[4] + num[2];
int ans6 = num[5] + num[6] + num[7] + num[8];
return ans1 == ans2 && ans1 == ans3 && ans1 == ans4 && ans1 == ans5 && ans1 == ans6;
}
int main()
{
do
{
if (jude())
break;
} while (next_permutation(num, num + 9));
cout << num[3] << endl;
return 0;
}
8H:蚂蚁感冒(编程)
题目描述:
长100厘米的细长直杆子上有n只蚂蚁。它们的头有的朝左,有的朝右。 每只蚂蚁都只能沿着杆子向前爬,速度是1厘米/秒。当两只蚂蚁碰面时,它们会同时掉头往相反的方向爬行。这些蚂蚁中,有1只蚂蚁感冒了。并且在和其它蚂蚁碰面时,会把感冒传染给碰到的蚂蚁。请你计算,当所有蚂蚁都爬离杆子时,有多少只蚂蚁患上了感冒。
【数据格式】
第一行输入一个整数n (1 < n < 50), 表示蚂蚁的总数。
接着的一行是n个用空格分开的整数 Xi (-100 < Xi < 100), Xi的绝对值,表示蚂蚁离开杆子左边端点的距离。正值表示头朝右,负值表示头朝左,数据中不会出现0值,也不会出现两只蚂蚁占用同一位置。其中,第一个数据代表的蚂蚁感冒了。
要求输出1个整数,表示最后感冒蚂蚁的数目。例如,输入:
3
5 -2 8
程序应输出:
1再例如,输入:
5
-10 8 -20 12 25
程序应输出:
3资源约定:
峰值内存消耗 < 256M
CPU消耗 < 1000ms
解析代码
题目分析:
此题目可简单化为将掉头看做为擦肩而过,然后进行模拟计算就可
#include<iostream>
using namespace std;
int a[55];
int main()
{
int n, i;
cin >> n;
for (i = 0; i < n; i++)
{
cin >> a[i];
}
for (i = 0; i < n - 1; i++) // 判断是否同一方向
{
if (a[i] * a[i + 1] < 0) // 有不同方向的
break;
}
if (i == n - 1) // 没有不同方向的
cout << 1 << endl;
int sum = 1;
for (i = 1; i < n; i++)
{
if (abs(a[i]) < abs(a[0]) && a[i] > 0)
++sum; // 第一个坐标左侧,并且方向是右边的会感染
if (abs(a[i]) > abs(a[0]) && a[i] < 0)
++sum; // 第一个坐标右侧,并且方向是左边的会感染
}
cout << sum << endl;
return 0;
}
9I:地宫取宝(编程)
题目描述:
X 国王有一个地宫宝库。是 n x m 个格子的矩阵。每个格子放一件宝贝。每个宝贝贴着价值标签。地宫的入口在左上角,出口在右下角。小明被带到地宫的入口,国王要求他只能向右或向下行走。走过某个格子时,如果那个格子中的宝贝价值比小明手中任意宝贝价值都大,小明就可以拿起它(当然,也可以不拿)。当小明走到出口时,如果他手中的宝贝恰好是k件,则这些宝贝就可以送给小明。请你帮小明算一算,在给定的局面下,他有多少种不同的行动方案能获得这k件宝贝。
【数据格式】
输入一行3个整数,用空格分开:n m k (1<=n,m<=50, 1<=k<=12)接下来有 n 行数据,每行有 m 个整数 Ci (0<=Ci<=12)代表这个格子上的宝物的价值要求输出一个整数,表示正好取k个宝贝的行动方案数。该数字可能很大,输出它对 1000000007 取模的结果。
例如,输入:
2 2 2
1 2
2 1
程序应该输出:
2
再例如,输入:
2 3 2
1 2 3
2 1 5
程序应该输出:
14
资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗 < 2000ms
解析代码
题目解析:先用dfs写,然后改成记忆化搜索:
//#include <bits/stdc++.h>
#include<iostream>
#include<cstring>
using namespace std;
const int MAX = 55;
const int K = 15;
const int MOD = 1000000007;
int n, m, k;
int arr[MAX][MAX];
int dp[MAX][MAX][K][K]; // dp[x][y][num][max]=n表示当在位置x,y时,其手上共有num件宝物
// 其中价值最大的是max,从这个位置到终点共有n种取法(走的路线和拿的策略)
int dfs(int x, int y, int num, int maxValue)
{
// 记忆化(若在之前遍历过而已经得的了这个情况下的数据时,就不再继续dfs下去了)
if (dp[x][y][num][maxValue + 1] != -1) // 由于maxValue的初始值必须为-1,在索引里最小为0,因此需要+1
return dp[x][y][num][maxValue + 1];
if (x == n && y == m) // 当到达出口时判断
{
if (num == k || num == k - 1 && arr[x][y] > maxValue)
return 1;
return 0;
}
//两种行走方式
long long sum = 0;
if (x + 1 <= n) // 如果可以往下走
{
if (arr[x][y] > maxValue) // 如果当前手中宝物最大值小于当前正在的这个点的宝物价值
sum += dfs(x + 1, y, num + 1, arr[x][y]); // 则可以拿
sum += dfs(x + 1, y, num, maxValue); // 也可以不拿
}
if (y + 1 <= m) // 如果可以往右走
{
if (arr[x][y] > maxValue)
sum += dfs(x, y + 1, num + 1, arr[x][y]);
sum += dfs(x, y + 1, num, maxValue);
}
return dp[x][y][num][maxValue + 1] = sum % MOD;
}
int main()
{
cin >> n >> m >> k;
for (int i = 1;i <= n;i++)
{
for (int j = 1;j <= m;j++)
{
cin >> arr[i][j];
}
}
memset(dp, -1, sizeof(dp));
dfs(1, 1, 0, -1); // 初始价值必须为-1,因为宝物的最小值是包括了0的
cout << dp[1][1][0][0] << endl;
return 0;
}
10J:小朋友排队(编程)
题目描述:
n 个小朋友站成一排。现在要把他们按身高从低到高的顺序排列,但是每次只能交换位置相邻的两个小朋友。每个小朋友都有一个不高兴的程度。开始的时候,所有小朋友的不高兴程度都是0。如果某个小朋友第一次被要求交换,则他的不高兴程度增加1,如果第二次要求他交换,则他的不高兴程度增加2(即不高兴程度为3),依次类推。当要求某个小朋友第k次交换时,他的不高兴程度增加k。请问,要让所有小朋友按从低到高排队,他们的不高兴程度之和最小是多少。如果有两个小朋友身高一样,则他们谁站在谁前面是没有关系的。
【数据格式】
输入的第一行包含一个整数n,表示小朋友的个数。第二行包含 n 个整数 H1 H2 … Hn,分别表示每个小朋友的身高。输出一行,包含一个整数,表示小朋友的不高兴程度和的最小值。
例如,输入:
3
3 2 1
程序应该输出:
9
【样例说明】
首先交换身高为3和2的小朋友,再交换身高为3和1的小朋友,再交换身高为2和1的小朋友,每个小朋友的不高兴程度都是3,总和为9。
【数据规模与约定】
对于10%的数据, 1<=n<=10;
对于30%的数据, 1<=n<=1000;
对于50%的数据, 1<=n<=10000;
对于100%的数据,1<=n<=100000,0<=Hi<=1000000。
解析代码
简而言之就是给你一个乱序序列,之后让你求最少付出多少代价就可以使得整个序列为有序序列,我们最开始想到的一定是归并排序,很好,这说明我们对这个问题是有所了解的,但是,因为归并排序只是简单求逆序对数量,我们无法直到最终每个小朋友的交换次数,所以这种方法是行不通的。
那么现在问题的核心在于如何快速求每个小朋友对应的逆序对数量(前面比他高的与后面比他矮的数量之和),遇事不决,肯定可以暴力去做,但是暴力是双重for循环,肯定会超时,所以有没有一种快速求得某个区间中的比某个数字大的个数的方法呢,这时我们可以使用树状数组。
树状数组适用于求某个区间和,同时可以动态的进行添加,我们结合这两个性质思考一下,由于所有的小朋友数量是固定的,我们可以按照下面的步骤求小朋友数量:
- 首先按顺序读入,每次读入一个,就查询前面比这个小朋友高的数量,之后将这个小朋友的身高添加进去
- 之后逆序读入,每次读入一个,就查询后面比这个小朋友身高矮的数量,之后将这个小朋友的身高添加进去
按照这样的步骤我们就可以求出每个小朋友的交换次数,之后求1+2+3+...+n即可,这里可以用等差数列性质求解。
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long LL;
//本题每个小朋友交换的次数就是每个小朋友前后逆序对的数量
//所以结果就是求出每个小朋友逆序对的数量并进行累加
//但是问题核心就在于如何求出每个小朋友对应的逆序对数量
const int N = 1e6 + 10;
int tr[N], sum[N], h[N];
//tr为每个小朋友前面比他身高高的人的数量或者后面比他身高矮的数量,会进行实时添加
int n;
// 下面三个为树状数组常用函数
int lowbit(int x)
{
return x & -x;
}
void add(int u, int v)
{
for (int i = u; i < N; i += lowbit(i))
{
tr[i] += v;
}
}
int query(int x)
{
int res = 0;
for (int i = x; i > 0; i -= lowbit(i))
{
res += tr[i];
}
return res;
}
int main()
{
cin >> n;
for (int i = 1; i <= n; ++i) // 还有身高为0的小朋友
{
scanf("%d", &h[i]);
++h[i];
}
for (int i = 1; i <= n; ++i) // 先统计前面比他身高高的小朋友数量
{
sum[i] += query(N - 1) - query(h[i]); // 小于最大身高数量-当前小朋友身高数量
add(h[i], 1);
}
memset(tr, 0, sizeof(tr)); // 之后统计后面比他身高矮的小朋友
for (int i = n; i >= 1; --i) // 这里要清空tr数组
{
sum[i] += query(h[i] - 1);
add(h[i], 1);
}
LL res = 0;
for (int i = 1; i <= n; ++i) // 一共交换sum[i]次,等差数列求和公式
{
res += (LL)sum[i] * (sum[i] + 1) / 2;
}
printf("%lld", res);
return 0;
}
/*
3
3 2 1
*/