文章目录
2019
1.组队(可以直接看)
或打表遍历,按ans最大更新 ,相等时可以 if(j == i)continue 跳过
2.年号字串 26进制 可以excel表 、 计算器 、编程
#include <iostream>
using namespace std;
int test_01() {
int num = 702; //zz 26*26 肯定是三位数,702初始值减少时间
for(char x = 'A'; x <= 'Z'; x++) {
for(char y = 'A'; y <= 'Z'; y++) {
for(char z = 'A'; z <= 'Z'; z++) {
num++;
if(num == 2019) {
cout << x << y << z << endl;
break; //停止循环减少时间
}
}
}
}
return 0;
}
3.数列求值
f(n) = f(n - 3) + f(n - 3) + f(n - 2) + f(n - 1)
第20190325项的最后4位 :4659
int test_02() {
int a = 1,b = 1,c = 1;
int d = 0,n = 4;
while(n != 20190325) {
d = (a + b + c)%10000; //除10^n^看做移小数点n位,取最后一位(个位) %10
n++;
a = b;
b = c;
c = d;
}
cout << d;
return 0;
}
//法二 开数组
typedef long long ll;
const int maxn = 20190325;
ll a_03[20190330];
int test_03() {
a_03[1] = a_03[2] = a_03[3] = 1;
for(int i = 4; i < 20190325; i++) {
a_03[i] = a_03[i - 1] + a_03[i - 2] + a_03[i - 3];
a_03[i] %= 10000;
}
cout << a_03[20190324] << endl;
return 0;
}
4.数的分解
2019分解成3个各不相同的数,且要求每个正整数都不包含数字2和4 (正整数没有0)
有多少种 :40785
bool check_04(int x) {
int t = x;
while(t) { //t==0时停止
if(t % 10 == 2 || t % 10 == 4)return false;
t /= 10;
}
return true;
}
int test_04() {
int ans = 0;
for(int x = 1; x < 2019; x++) { //正整数
for(int y = x + 1; y < 2019; y++) { //各不相等
int z = 2019 - x - y; //和为0
if(check_04(x)&&check_04(y)&&check_04(z)&&(z > y)) { //内层最先遍历完 因为z减少循环确定2019,不需要再加条件(x + y + z) == 2019&&
ans++;
}
}
}
cout << ans << endl;
return 0;
}
补:蛇形矩阵找规律 对角线 1 5 13 25 41 即 ans += 4*i
可以先n= 5时 , 看ans是不是41验证算法的正确性
2013
1.猜年龄 (年轻就出席会议)
年龄3 为4位数 与年龄4 为6位数 ,这两个结果各个位刚好包含了0 - 9 十个数字,求年龄
(枚举范围自己先判断减小!)
int test_05() {
for(int i = 10; i < 30; i++) {
int i1 = i * i* i;
int i2 = i*i*i*i;
if(i1>=1000 && i1 <10000 && i2>= 100000 && i2 < 1000000) {
cout<<i << " " << i1 <<" "<< i2 << endl; //肉眼观察是不是 0-9都有
}
}
return 0; //发现符合18 5832 104976 --18岁
}
马虎的算式
ab * cde == abd * ce
设 abcde代表1 – 9 的5个不同数字
题目的提示:满足交换律,累加为不同种类,答案一定为偶数
答案:
142
#include<cstdio>
int test_06() {
int ans = 0;
for(int a = 1; a <= 9; a++) {
for(int b = 1; b <= 9; b++) {
if(a== b)continue;
for(int c = 1; c <= 9; c++) {
if(c == b || c== a )continue; //之前已经保证 a != b ,这里等效成 if(c != b && c != a)for(){}
for(int d = 1; d <= 9; d++) {
if(d == c || d== b || d== a )continue;
for(int e = 1; e <= 9; e++) {
if(e == d || e == c || e == b || e == a )continue;
if( (a*10 + b)*(c*100+d*10+e) == (a*100+d*10+b)*(c*10 + e) ) { //这里printf验算
ans++;
}
}
}
}
}
}
cout << ans << endl;
return 0;
}
振兴中华
每道题先看看有没有规律!
从我做起振
我做起振兴
做起振兴中
起振兴中华只能上下左右跳相邻格子
遍历出:
从我做起振兴中华
问有几种跳跃路线
枚举:
1.迭代
2.递归
/*.......my思考
string maze[4];
string s1 ="从我做起振兴中华";
int d[4][2]= {{1,0},{0,1},{-1,0},{0,-1}};
bool in(int x,int y){
if(x >= 0 && x < 5 && y >= 0 && y < 5){
return true;
}
return false;
}
void dfs(int x,int y){ //(0,0)
for(int i = 0;i < 4;i++){
int tx = x + d[i][0];
int ty = y + d[i][1];
if(in(tx,ty) && )
}
}
int test_07(){
for(int i = 0;i < 4;i++){
cin >> maze[i] << endl;
}
}
*/
//递归 先看有没有规律!!!
int f(int x,int y) {
if(x == 3 || y == 4)return 1;
return f(x + 1,y) + f(x , y + 1); //能发现规律地图是有规律的!!! 可以dp
}
int dp[5][5];
int test_07() {
cout << f(0,0) << endl;
return 0;
}
幻方填空 – 简单+耐心 (全排列)2n-1
1 – 16 填在 4 * 4 方格中
使得行、列、两条对角线上的数字之和都相等 ∑d[i][0] = ∑d[0][j] = ∑d[i][i] 写个check检查!
给出矩阵:
16 ? ? 13
? ? 11 ?
9 ? ? *
? 15 ? 1
计算出 ? 和 * 并提交 *
答案是1个整数
暴力法:10个数全排列 放入试试 填空题不考虑时间
c++自带的全排列函数: next_permutation() !!!!!
next_permutation()用法:
next_permutation按照字典序升序趋势,从当前状态向后做全排列。 数组顺序后移
也即,如果导入2,1,3.函数只会从213开始向后找其他排列。
如231,312,321。因此若要求某个序列的全排列,应当先升序排列,再导入
#include<vector>
#include<algorithm>
// 1 9 11 13 15 16
int a[] = {2,3,4,5,6,7,8,10,12,14}; //剩余未选择的元素
/*
16 ? ? 13
? ? 11 ?
9 ? ? *
? 15 ? 1
*/
void check(vector<int> arr) {
int r1 = 29 + arr[0] + arr[1];
int r2 = 11 + arr[2] + arr[3] + arr[4];
int r3 = 9 + arr[5] + arr[6] + arr[7];
int r4 = 16 + arr[8] + arr[9];
int c1 = 25 + arr[2] + arr[8]; //按行填空的顺序
int c2 = 15 + arr[0] + arr[3] + arr[5];
int c3 = 11 + arr[1] + arr[6] + arr[9];
int c4 = 14 + arr[4] + arr[7];
int r = 17 + arr[3] + arr[6]; //下标从0开始第4个为下标 3
int l = 24 + arr[5] + arr[8];
if(r1 == r2 && r2 == r3 && r3 == r4&& c1 == c2&& c2 == c3&& c3 == c4&& r == l) {
cout << arr[7] << endl;
/*有时间可验证*/
for(int i = 0; i < 10; i++) {
cout <<arr[i] << " ";
}
}
}
int test_08() {
vector<int> arr;
for(int i = 0; i < 10; i++) {
arr.push_back(a[i]);
}
do {
check(arr);
} while(next_permutation(arr.begin(),arr.end())); //全排列遍历所有情况
return 0;
}
公约数公倍数 (代码填空题)
填代码 坑点: a,b值已经变化,中间用m,n拷贝了 所有
应该填 m / 最大公约数 * n ( 防止爆炸,最大公约数代码中为b )
三步排序 (代码填空题)
负数靠左边,正数靠右边,0中间,负的与正的各自区域不要求有序
(快速排序变种题 – 小的移到左边,大的移到右边)
代码填空题(复制,看懂代码,填写代码,跑):
等于0的不动left指向第一个大等于0的, 填 mod++,//指向下一个区域
核桃的数量
读题目,对数字敏感
带领3个开发组
1.各组的核桃数量必须相同
2.组内必须能平分核桃
3.尽量满足1 ,2条件的最小数量
输入每组人数
2 4 5
输出一个正整数,表示每袋的核桃数量
20
int test_09() {
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
for(int i = 1; i <= a * b * c; i++) { //到a * b * c时,必能整除a ,b , c
if(i % a == 0 && i % b == 0 && i % c == 0 ) {
printf("%d\n",i);
break;
}
}
return 0;
}
打印十字图 (相对代码量巨大)
代码多
分析:
①1,2,3,4子问题不同 , 一段一段写
②1,2,3,4子问题类似 , 递归,迭代
每一圈模拟找规律
中间十字
数组空间 9 + (4 * (n - 1))2
从最外圈到1 (最内层十字)
每圈的每行找规律
一行:等差数列 5 - 9 -13 5 + (n - 1) * 4
二行: v1 - 2
三行
第四个部分(列连续的)
第五个部分 (与二行相同)
第六个部分(与一行相同)
#include<iostream>
using namespace std;
char arr_10[9 + 4 * 28][9 + 4 * 28];
void printAll_10(int l,int r) {
for(int i = l; i <= r; i++) {
for(int j = l; j <= r; j++) {
if(arr_10[i][j] != '$') {
arr_10[i][j] = '.';
}
cout << arr_10[i][j] << " ";
}
cout << endl;
}
}
/*现在输出最外层: (此题特点,代码量大,若不是时间充裕,建议留到最后)
. . $ $ $ $ $ . .
. . $ . . . $ . .
$ $ $ . . . $ $ $
$ . . . . . . . $
$ . . . . . . . $
$ . . . . . . . $
$ $ $ . . . $ $ $
. . $ . . . $ . .
. . $ $ $ $ $ . .
*/
//只打印一圈 , 非完整题解!
int test_10() {
int l = 0,r; //每层就是l,r不一样 (2013 8-2...)
int n;
scanf("%d",&n);
r = 9 + 4 * (n - 1) - 1; //可以取到的最大 下标
//处理第一行,最后一行
for(int i = 2; i <= r - 2; i++) {
arr_10[l][i] = '$';
arr_10[r][i] = '$';
}
//处理第二行,倒数第二行
arr_10[1][2] = '$' ;
arr_10[1][r - 2] = '$';
arr_10[r-1][2] = '$';
arr_10[r-1][r - 2] = '$';
//处理第三行和倒数第三行
arr_10[2][0] = '$';
arr_10[2][1] = '$';
arr_10[2][2] = '$';
arr_10[2][r] = '$';
arr_10[2][r-1] = '$';
arr_10[2][r-2] = '$';
arr_10[r-2][0] = '$';
arr_10[r-2][1] = '$';
arr_10[r-2][2] = '$';
arr_10[r-2][r] = '$';
arr_10[r-2][r-1] = '$';
arr_10[r-2][r-2] = '$';
//处理两边 (第4列到倒数第4列)
for(int j = 3; j <= r-3; j++) {
arr_10[j][0] = '$';
arr_10[j][r] = '$';
}
printAll_10(l,r);
return 0;
}
带分数
100 = 3 + 69258 / 714
还可以表示 100 = 82 + 3546 / 197
带分数特点: 数字1-9,分别出现且只出现一次(不包含0)
100 有 11种表示法
输入 N <1e6
输出 表示的方法数量
①枚举 1-98 + ()/() 限制:不重复数字
②全排列思考 ,插入+ 和 /
生成1~9的全排列,先在可能位置插入+ ,再在可能位置插入/ ,验算等式,计数
next_permutation()
头文件algorithm
do{//全排列模板}
while(next_permutation(s.begin(),s.end()));
#include<iostream>
#include<string>
#include<cstdlib> //c_str()
#include<algorithm>
using namespace std;
//此处超时 但 可以 打表 ,还可以parse函数改造
int test_11() { // !!!!! 截取字符串会拷贝(也有一层循环!),反复开辟空间 的时间耗时 !!!!! 比较的常数时间很少
int n,ans = 0; // substr能不用就不用!!!!!!
scanf("%d",&n);
string s = "123456789"; //+最多到7 ,后面要给 / 留位置
do { //全排列模板
for(int i = 1; i <= 7; i++) { //+号前面的串长度
string a = s.substr(0,i); // 加号前面的数
int inta = atoi(a.c_str()); //atoi字符串转整数int , 需要先转c_str
if(inta >= n)break; //可行性剪枝 先想想哪些不可能的就先排除不用往下算了(不能大于n)
for(int j = 1; j <= 9 - i - 1; j++) { // 两个符号之间的串的长度 (除号后面必须有数)
string b = s.substr(i,j); // 除号前面的串 从i开始到j
string c = s.substr(i + j); //除号后面的串 从某个位置开始截取到结束
int intb = atoi(b.c_str());
int intc = atoi(c.c_str());
if(intb % intc == 0 && inta + intb / intc == n) {
ans++;
}
}
}
} while(next_permutation(s.begin(),s.end()));
printf("%d\n",ans);
return 0;
}
剪格子
m*n 的格子是否可以切割成 两个部分,使得各个部分的数字和相等
如果存在多解,输出左上角格子的那个区域包含格子最小的数目
如果无分割,输出0
输入 (m列 n行 数值)
3 3
10 1 52
20 30 1
1 2 3
输出: (满足条件左上区域最小格子数)
3
输入2:
4 3
1 1 1 1
1 30 80 2
1 1 1 100
输出2:
10
思路: 遍历(走迷宫)累加走过的数值 == 总值/2 就等效满足
本质:图的连通 --> DFS (算法基础题:水洼)
过程中 > c/2 break; 递归+回溯+剪枝
#include<algorithm> //min
int m,n,total;
int g[10][10];
int ans = 100; //最多 n*m 上限100
int d[4][2] = {{1,0},{0,1},{-1,0},{0,-1}};
bool vis[10][10];
bool in(int x,int y) {
if(0 <= x && x < n && y >= 0 && y < m) {
return true;
}
return false;
}
void dfs(int i,int j,int sum,int cnt) {
if(sum > total/2)return;
if(sum == total/2) {
ans = min(ans , cnt);
}
vis[i][j] = true;
//四个方向 (与迷宫走法有区别,也是考虑路径最优 ,可能蛇形走位...)
if(i + 1 < n && !vis[i + 1][j])dfs(i + 1,j,sum + g[i][j], cnt + 1); //可以往下走的条件
if(i - 1 >= 0 && !vis[i - 1][j])dfs(i - 1,j,sum + g[i][j], cnt + 1); //可以往上走
if(j + 1 < m && !vis[i][j + 1])dfs(i,j + 1,sum + g[i][j], cnt + 1); //可以往右走
if(j - 1 >= 0 && !vis[i][j - 1])dfs(i,j - 1,sum + g[i][j], cnt + 1); //可以往左走
/*
for(int k; k < 4; k++) { //bug!!
int tx = i + d[k][0];
int ty = j + d[k][1];
if(in(tx,ty)&&!vis[tx][ty]) {
dfs(tx,ty,sum + g[i][j],cnt + 1);
}
}
*/
vis[i][j] = false;//回溯
}
//不足:若
/*
2 2
1 1
1 3
0 因为 1 1 dfs不会回溯到另一个1,认为无解,输出0
*/
int test_12() {
scanf("%d%d",&m,&n); //题坑 ,先输入列 再输入行
for(int i = 0; i < n; i++) {
for(int j = 0; j < m; j++) {
scanf("%d",&g[i][j]);
total += g[i][j];
}
}
dfs(0,0,0,0);
if(ans != 100){
printf("%d\n",ans);
}
else{
printf("%d\n",0);
}
return 0;
}
2013年真题小结
01 猜年龄 枚举
02 马虎的算式 枚举加验证 ab*cde = aeb * df
03 振兴中华 递归
04 幻方填空 全排列(集合有序)
05 公约数公倍数
06 三步排序 快速排序 (三指针)的变体
07 核桃的数量 简单枚举
08 打印十字图 先写死再写活 (有规律)
09 带分数 全排列 + 枚举加号和除号的插入位置 -> 检查(剪枝) ,手工写字符串转整数
10 剪格子 深搜 + 回溯 + 剪枝 (要点:能想到sum == total/2 为结束条件)
int main() {
test_12();
return 0;
}