目录
持续更新中...
一、暴力枚举例题
1.蓝桥杯2015A2奇妙的数字
小明发现了一个奇妙的数字。
它的平方和立方正好把0~9的10个数字每个用且只用了一次。
你能猜出这个数字是多少吗?
#include <iostream>
#include <string>
#include <sstream>
#include <set>
using namespace std;
// 整型转换为字符型
void i2s(int num, string &str) {
stringstream ss;
ss << num;
ss >> str;
}
bool check(string s) {
set<char> ss;
for (int i = 0; i < s.length(); i++) {
ss.insert(s[i]); // 向集合中插入数字,数字无重复
}
return s.size() == 10 && ss.size() == 10; // 平方和立方的字符串相连长度为10且集合长度为10
}
int main() {
for (int i = 69; i < 1000; i++) {
string s1, s2;
i2s(i * i, s1);
i2s(i * i * i, s2);
if (check(s1 + s2)) { // 字符串相加为相连
cout << i << endl;
break;
}
}
return 0;
}
2.蓝桥杯2015A7手链样式
小明有3颗红珊瑚,4颗白珊瑚,5颗黄玛瑙。
他想用它们串成一圈作为手链,送给女朋友。
现在小明想知道:如果考虑手链可以随意转动或翻转,一共可以有多少不同的组合样式呢?
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int main() {
string s = "aaabbbbccccc";
vector<string> v1;
int ans = 0;
do {
// 排除重复,对于v1中的每个元素进行检查,如果存在s的旋转或者翻转,则跳过
int i;
for (i = 0; i < v1.size(); i++) {
if (v1[i].find(s) != string::npos) // string::npos: 不存在 // 找到s的旋转或翻转
break;
}
// s不可用的情况
if (i != v1.size()) continue;
string s2 = s + s;
v1.push_back(s2); // 便于判断旋转的情况
reverse(s2.begin(), s2.end());
v1.push_back(s2); // 将s的翻转放入vector中
ans++;
}while(next_permutation(s.begin(), s.end())); // 全排列
cout << ans << endl;
return 0;
}
3.蓝桥杯2016A8四平方和
四平方和定理,又称为拉格朗日定理。
每个整数都能表示为至多4个数的平方和。
如果把0算进去,就正好可以表示为4个数的平方和。
比如: 5 = 0^2 + 0^2 + 1^2 + 2^2 7 = 1^2 + 1^2 + 1^2 + 2^2
对于给定的一个正整数,可能存在多种平方和的表示方法。
要求你对4个数进行排序:0<=a<=b<=c<=d,并对所有的可能表示法按a,b,c,d为联合主键升序排列,最后输出第一个表示法。
程序输入为一个整数N(N<5 000 000),要求输出4个非负整数,按从小到大排列, 中间用空格分开。
例如,输入773535,输出 1 1 267 838。
#include <cstdio>
#include <map>
#include <cmath>
using namespace std;
// 将4个数转换为2个数加2个数,判断某个数是否可以表示为2个数的平方和
int N;
map<int, int> cache; // 用map表示缓存
int main() {
scanf("%d", &N);
for (int c = 0; c * c <= N / 2; c++) {
for (int d = c; c * c + d * d <= N; d++) {
if (cache.find(c * c + d * d) == cache.end()) { // 未找到
cache[c * c + d * d] = c; // 注意此处将能查出来的平方和作为下标,值为c
}
}
}
for (int a = 0; a * a <= N / 4; a++) {
for (int b = a; a * a + b * b <= N / 2; b++) {
if (cache.find(N - a * a - b * b) != cache.end()) { // 找到
int c = cache[N - a * a - b * b];
int d = int(sqrt(N - a * a - b * b - c * c));
printf("%d %d %d %d", a, b, c, d);
return 0;
}
}
}
}
二、dfs
1.蓝桥杯2015A6牌型总数
小明被劫持到X赌城,被迫与其他3人玩牌。
一副扑克牌(去掉大小王牌,共52张),均匀发给4个人,每个人13张。
这时,小明脑子里突然冒出一个问题:
如果不考虑花色,只考虑点数,也不考虑自己得到的牌的先后顺序,
自己手里能拿到的初始牌型组合一共有多少种呢?
请填写该整数,不要填写任何多余的内容或说明文字。
// 一共13张牌,每种牌的个数为0,1,2,3,共多少种组合
#include <iostream>
#include <sstream>
using namespace std;
int ans = 0;
void f(int k, int cnt) { // k代表牌型,cnt代表牌的总数
if (cnt > 13 || k > 13) return; // 结束点:牌型>13或牌的总数>13
if (k == 13 && cnt == 13) { // 结束点:牌型=13(到达牌型终点)且牌的总数=13
ans++;
return;
}
for (int i = 0; i < 5; i++) {
f(k + 1, cnt + i);
}
}
void i2s(int num, string &str) {
stringstream ss;
ss << num;
ss >> str;
}
int main() {
f(0, 0);
cout << ans << endl;
return 0;
}
2.蓝桥杯2017A1迷宫
X星球的一处迷宫游乐场建在某个小山坡上。它是由10x10相互连通的小房间组成的。
房间的地板上写着一个很大的字母。我们假设玩家是面朝上坡的方向站立,则:
L表示走到左边的房间,
R表示走到右边的房间,
U表示走到上坡方向的房间,
D表示走到下坡方向的房间。
开始的时候,直升机把100名玩家放入一个个小房间内。玩家一定要按照地上的字母移动。
迷宫地图如下:
------------
UDDLUULRUL
UURLLLRRRU
RRUURLDLRD
RUDDDDUUUU
URUDLLRRUU
DURLRLDLRL
ULLURLLRDU
RDLULLRDDD
UUDDUDUDLL
ULRDLUURRR
------------
请你计算一下,最后,有多少玩家会走出迷宫? 而不是在里边兜圈子。
#include <iostream>
#include <cstring>
using namespace std;
// dfs+标记(防止绕圈子)
string data1[10]; // 改为data1防止重名
int vis[10][10];
int ans;
bool solve(int i, int j) {
if (i < 0 || i > 9 || j < 0 || j > 9)
return true;
if (vis[i][j] == 1)
return false;
vis[i][j] = 1;
switch(data1[i][j]) {
case 'U':
return solve(i - 1, j);
case 'D':
return solve(i + 1, j);
case 'L':
return solve(i, j - 1);
case 'R':
return solve(i, j + 1);
default:
return false;
}
}
int main() {
data1[0] = "UDDLUULRUL";
data1[1] = "UURLLLRRRU";
data1[2] = "RRUURLDLRD";
data1[3] = "RUDDDDUUUU";
data1[4] = "URUDLLRRUU";
data1[5] = "DURLRLDLRL";
data1[6] = "ULLURLLRDU";
data1[7] = "RDLULLRDDD";
data1[8] = "UUDDUDUDLL";
data1[9] = "ULRDLUURRR";
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 10; j++) {
memset(vis, 0, sizeof(vis));
bool res = solve(i, j);
if (res)
ans++;
}
}
cout << ans << endl;
return 0;
}
三、回溯
1.蓝桥杯2016A3方格填数
如下的10个格子
+--+--+--+
| | | |
+--+--+--+--+
| | | | |
+--+--+--+--+
| | | |
+--+--+--+
填入0~9的数字。
要求:连续的两个数字不能相邻。
(左右、上下、对角都算相邻)
一共有多少种可能的填数方案?
请填写表示方案数目的整数。
#include <iostream>
using namespace std;
int ans = 0;
int a[10] = {0,1, 2, 3, 4, 5, 6, 7, 8, 9};
bool check() {
if (
abs(a[0] - a[1]) == 1 ||
abs(a[0] - a[3]) == 1 ||
abs(a[0] - a[4]) == 1 ||
abs(a[0] - a[5]) == 1 ||
abs(a[1] - a[2]) == 1 ||
abs(a[1] - a[4]) == 1 ||
abs(a[1] - a[5]) == 1 ||
abs(a[1] - a[6]) == 1 ||
abs(a[2] - a[5]) == 1 ||
abs(a[2] - a[6]) == 1 ||
abs(a[3] - a[4]) == 1 ||
abs(a[3] - a[7]) == 1 ||
abs(a[3] - a[8]) == 1 ||
abs(a[4] - a[5]) == 1 ||
abs(a[4] - a[7]) == 1 ||
abs(a[4] - a[8]) == 1 ||
abs(a[4] - a[9]) == 1 ||
abs(a[5] - a[6]) == 1 ||
abs(a[5] - a[8]) == 1 ||
abs(a[5] - a[9]) == 1 ||
abs(a[6] - a[9]) == 1 ||
abs(a[7] - a[8]) == 1 ||
abs(a[8] - a[9]) == 1
) {
return false;
}
return true;
}
// 考虑第k个位置,一般从0开始
void f(int k) {
// 出口
if (k == 10) {
bool b = check();
if (b) {
ans++;
}
return; // 注意这里要return
}
for (int i = k; i < 10; i++) {
// 尝试将位置i与位置k交换,以此确定k位的值
int t = a[i];
a[i] = a[k];
a[k] = t;
f(k + 1);
// 回溯
t = a[i];
a[i] = a[k];
a[k] = t;
}
}
int main() {
f(0);
cout << ans << endl;
return 0;
}
2.蓝桥杯2016A7剪邮票
如【图1.jpg】, 有12张连在一起的12生肖的邮票。
现在你要从中剪下5张来,要求必须是连着的。
(仅仅连接一个角不算相连) 比如,【图2.jpg】,【图3.jpg】中,粉红色所示部分就是合格的剪取。
// 想法1:每个格子作为起点,dfs连5张,去重:对于T字型失效
// 想法2:枚举所有的5张牌的组合(全排列),检查它们是不是一个连通块(dfs)
#include <iostream>
#include <set>
using namespace std;
int ans = 0;
int a[] = {0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1}; // 它的全排列代表着12选5的一个方案
void dfs(int g[3][4], int i, int j) {
g[i][j] = 0;
if (i - 1 >= 0 && g[i - 1][j] == 1) {
dfs(g, i - 1, j);
}
else if (i + 1 <= 2 && g[i + 1][j] == 1) {
dfs(g, i + 1, j);
}
else if (j - 1 >= 0 && g[i][j - 1] == 1) {
dfs(g, i, j - 1);
}
else if (j + 1 <= 3 && g[i][j + 1] == 1) {
dfs(g, i, j + 1);
}
}
bool check() {
int g[3][4];
// 将某个排列映射到二维矩阵上
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 4; j++) {
if (a[i * 4 + j]) g[i][j] = 1;
else g[i][j] = 0;
}
}
int cnt = 0; // 连通块的数目
// g上面有5个格子被标记为1,现在采用dfs做连通性检查,要求只有一个连通块
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 4; j++) {
if (g[i][j] == 1) {
dfs(g, i, j);
cnt++;
}
}
}
return cnt = 1; // 判断连通块是否只有一个,否则有多个分区
}
set<string> s1;
void i2s(string &s) {
for (int i = 0; i < 12; i++) {
s.insert(s.end(), a[i] + '0');
}
}
// 由于不同的0排列和1排列是相同的,所以此处需要去重
bool isExist() {
string a_str;
i2s(a_str);
if (s1.find(a_str) == s1.end()) { // 未找到
s1.insert(a_str);
return false;
}
else {
return true;
}
}
void f(int k) {
if (k == 12) {
if (!isExist() && check()) { // 去除重复情况
ans++;
}
}
for (int i = k; i < 12; i++) {
{int t = a[i]; a[i] = a[k]; a[k] = t;}
f(k + 1);
{int t = a[i]; a[i] = a[k]; a[k] = t;}
}
}
int main() {
f(0);
cout << ans << endl;
return 0;
}
四、排序
1.蓝桥杯2016A4快速排序
排序在各种场合经常被用到。
快速排序是十分常用的高效率的算法。
其思想是:先选一个“标尺”, 用它把整个队列过一遍筛子,以保证:其左边的元素都不大于它,其右边的元素都不小于它。
这样,排序问题就被分割为两个子区间。再分别对子区间排序就可以了。
下面的代码是一种实现,请分析并填写划线部分缺少的代码。
#include <cstdio>
void swap(int a[], int i, int j) {
int t = a[i];
a[i] = a[j];
a[j] = t;
}
int partition(int a[], int p, int r) {
int i = p; // 该段左端点
int j = r + 1; // 该段右端点
int x = a[p]; // 左端点值
while (true) {
while (i < r && a[++i] < x); // i向右走,走到r之前,比x小的最右边,即指向第一个>x处
while (a[--j] > x); // j向左走,走到比x大的最左边,即指向第一个<x处
if (i >= j) break;
swap(a, i, j);
}
//填空
swap(a, p, j);
return j; // j为分界点
}
void quicksort(int a[], int p, int r) {
if(p<r) {
int q = partition(a,p,r);
quicksort(a,p,q-1);
quicksort(a,q+1,r);
}
}
int main() {
int i;
int a[] = {5,13,6,24,2,8,19,27,6,12,1,17};
int N = 12;
quicksort(a, 0, N-1);
for (i = 0; i < N; i++) printf("%d ", a[i]);
printf("\n");
return 0;
}
五、其他
1.蓝桥杯2016A6消除尾一
二进制位运算
下面的代码把一个整数的二进制表示的最右边的连续的1全部变成0 如果最后一位是0,则原数字保持不变。
如果采用代码中的测试数据,应该输出: 00000000000000000000000001100111 00000000000000000000000001100000 00000000000000000000000000001100 00000000000000000000000000001100。
请仔细阅读程序,填写划线部分缺少的代码。
#include <cstdio>
void f(int x) {
int i;
// 转换前
for ( i = 0; i < 32; i++) printf("%d", ( x >> (31 - i)) & 1);
printf(" ");
// 填空
x = x & (x + 1);
// 转换后
for(i = 0; i < 32; i++) printf("%d", (x >> (31 - i)) & 1);
printf("\n");
}
int main() {
f(103);
f(12);
return 0;
}