面试笔试算法-欧拉计划
面试笔试算法-二分专题
面试笔试算法-Oj刷题
面试笔试算法-LeetCode刷题
面试笔试算法-STL的使用
面试笔试算法之排列组合与搜索走地图问题
蓝色框表示3的倍数、绿框是5的倍数、黄框是重复项
利用等差数列分别求出3的倍数的和、5的倍数和相加然后减去重复项15的倍数和
即得结果。时间复杂度为O(1)
#include
using namespace std;
int main(int argc, char *grav[])
{
/* 第一种简单的方法,直接判断是否是3和5的倍数进行输出
int ans = 0;
for (int i = 1; i < 1000; i++) {
if (i % 3 == 0 || i % 5 == 0) {
ans += i;
}
}
cout << ans << endl;
*/
// 利用上述技巧编程
int t3 = (3 + 999) * 333 / 2;
int t5 = (5 + 995) * 199 / 2;
int t15 = (15 + 990) * 66 / 2;
cout << t3 + t5 - t15 << endl;
return 0;
}
只需要三个变量来回循环利用计可!不断更新a、b、c的值
#include
using namespace std;
int main(int argc, char *grav[])
{
int a = 1, b = 1, c = 2, ans = 0;
while (c <= 4000000) {
if (c % 2 == 0) {
ans += c;
}
c = b + c;
a = b;
b = c - b;
}
cout << ans << endl;
return 0;
}
首先定义一个临时的变量t用来存储反转后的数字
通过while循环来进行取余求模进行反转
如果t与原始的数据相等则是回文数
#include
using namespace std;
int func(int x) {
int t = 0, row = x;
while (x) {
t = t * 10 + x % 10;
x /= 10;
}
return t == row;
}
int main(int argc, char *grav[])
{
int ans = 0;
for (int i = 100; i < 1000; i++) {
for (int j = i; j < 1000; j++) {
int t = i * j;
if (func(t)) {
ans = max(ans, t);
}
}
}
cout << ans << endl;
return 0;
}
比较基础,直接计算
#include
using namespace std;
int main(int argc, char *grav[])
{
int sum = 0, psum = 0;
for (int i = 1; i <= 100; i++) {
sum += i;
psum += i * i;
}
cout << sum * sum - psum << endl;
return 0;
}
#include
using namespace std;
char num[1005];
long long ans, zero_cnt, now = 1;
int main(int argc, char *grav[])
{
cin >> num;
for (int i = 0; i < 1000; i++) {
if (i < 13) {
now *= num[i] - '0';
} else {
if (num[i] == '0') {
zero_cnt++;
} else {
now *= num[i] - '0';
}
if (num[i - 13] == '0') {
zero_cnt--;
} else {
now /= num[i - 13] - '0';
}
}
if (!zero_cnt) {
ans = max(ans, now);
}
}
cout << ans << endl;
return 0;
}
如上图所示:通过一个简单的矩阵推算出方向数组的八个方向,上(-1, 0)、下(1,0)、左(0,-1)、右(0,1)、右上(-1,1)、右下(1,1)、左下(1,-1)、左上(-1,-1)。
- 计算某个数各个方向的连续4个数的乘积最大值
计算问题:为了避免重复,只需要计算连续的4个方向进行计算。
边界问题:1、判断边界;2、边界补0(至少三圈0)
#include
using namespace std;
int dirx[4] = {
0, 1, 1, 1};
int diry[4] = {
1, 1, 0, -1};
int num[30][30], ans;
int main(int argc, char *grav[])
{
for (int i = 5; i < 25; i++) {
for (int j = 5; j < 25; j++) {
cin >> num[i][j];
}
}
for (int i = 5; i < 25; i++) {
for (int j = 5; j < 25; j++) {
// 遍历四个方向
for (int k = 0; k < 4; k++) {
int t = num[i][j];
// 四个方向的延伸的四个数
for (int l = 1; l < 4; l++) {
int x = i + dirx[k] * l;
int y = j + diry[k] * l;
t *= num[x][y];
}
// meic
ans = max(ans, t);
}
}
}
cout << ans << endl;
return 0;
}
将每次计算的递归结果放在一个数组中,下次递归如果数组有值直接返回,避免了重复计算。
递归 + 记忆化 ~= 递推
#include
using namespace std;
int num[1000000];
int func(long long n) {
if (n == 1) return 1;
// 如果数组中有就返回
if (n < 1000000 && num[n]) return num[n];
int t;
if (n % 2 == 0) {
t = func(n / 2) + 1;
} else {
t = func(3 * n + 1) + 1;
}
// 将每次计算的结果放在数组中,记忆数组
if (n < 1000000) {
num[n] = t;
}
return t;
}
int main(int argc, char *grav[])
{
int ans = 1;
for (int i = 2; i <= 1000000; i++) {
if (func(ans) < func(i)) {
ans = i;
}
}
cout << ans << endl;
return 0;
}
1、将数字1、2反向存在数组中,低位在前,高位在后,数组第一位存数字的长度。
2、定义一个结果数组,用来存储最终结果。
3、进行竖式相加,存储在对应结果数组的位置上。
4、处理进位。从前往后。
#include
#include
using namespace std;
int num1[105], num2[105], sum[105];
char s1[105], s2[105];
int main(int argc, char *grav[])
{
cin >> s1 >> s2;
num1[0] = strlen(s1);
num2[0] = strlen(s2);
// 数据处理
for (int i = 0, j = num1[0]; i < num1[0]; i++, j--) {
num1[j] = s1[i] - '0';
}
for (int i = 0, j = num2[0]; i < num2[0]; i++, j--) {
num2[j] = s2[i] - '0';
}
// 位数多的选为结果数组
sum[0] = max(num1[0], num2[0]);
for (int i = 1; i <= sum[0]; i++) {
sum[i] = num1[i] + num2[i];
}
for (int i = 1; i <= sum[0]; i++) {
// 处理进位
if (sum[i] > 9) {
sum[i + 1] += sum[i] / 10;
sum[i] %= 10;
// 如果最后一位有进位,则长度要加1
if (i == sum[0]) {
sum[0]++;
}
}
}
for (int i = sum[0]; i > 0; i--) {
cout << sum[i];
}
cout << endl;
return 0;
}
两个变量里的大整数循环加即可
int num[2] = {1}; //剩下的自动填充0
#include
using namespace std;
// 采用大整数加法
int func(int *n1, int *n2) {
n2[0] = n1[0];
for (int i = 1; i <= n2[0]; i++) {
n2[i] += n1[i];
if (n2[i] > 9) {
n2[i + 1] += n2[i] / 10;
n2[i] %= 10;
if (i == n2[0]) {
n2[0]++;
}
}
}
return n2[0] >= 1000;
}
int main(int argc, char *grav[])
{
int num[2][1100] = {
{
1, 1}, {
1, 1}}; //前两个数初始化位1
int a = 0, b = 1;
// 两个变量循环加,死循环
for (int i = 3; 1; i++) {
// 函数返回值为1,说明超过1000位
if (func(num[a], num[b]) == 1) {
cout << i << endl;
break;
}
swap(a, b);
}
return 0;
}
每次存放结果的位置位i + j - 1
#include
#include
using namespace std;
int main(int argc, char *grav[])
{
char s1[1005], s2[1005];
// 注意是int类型,char类型会溢出
int n1[1005] = {
0}, n2[1005] = {
0}, ans[1005] = {
0};
cin >> s1 >> s2;
n1[0] = strlen(s1), n2[0] = strlen(s2);
// i + j - 1
ans[0] = n1[0] + n2[0] - 1;
// 处理数据
for (int i = 1, j = n1[0] - 1; i <= n1[0]; i++, j--) {
n1[i] = s1[j] - '0';
}
for (int i = 1, j = n2[0] - 1; i <= n2[0]; i++, j--) {
n2[i] = s2[j] - '0';
}
// i + j - 1
for (int i = 1; i <= n1[0]; i++) {
for (int j = 1; j <= n2[0]; j++) {
ans[i + j - 1] += n1[i] * n2[j];
}
}
// 处理进位
for (int i = 1; i <= ans[0]; i++) {
if (ans[i] > 9) {
ans[i + 1] += ans[i] / 10;
ans[i] %= 10;
if (i == ans[0]) {
ans[0]++;
}
}
}
// 处理结果
for (int i = ans[0]; i > 0; i--) {
cout << ans[i];
}
cout << endl;
return 0;
}
1、递推:到达某一个点的路径数只能是从上和左边的路径s
#include
using namespace std;
int main(int argc, char *grav[])
{
// diyizho
long long dp[25][25] = {
0};
for (int i = 1; i <= 21; i++) {
for (int j = 1; j <=21; j++) {
// 左上角(1,1)初始值为1,需要特判赋值
if (i == 1 && j == 1) {
dp[i][j] = 1;
} else {
dp[i][j] = dp[i - 1][j] + dp[i][j-1];
}
}
}
cout << dp[21][21] << endl;
long long ans = 1;
for (int i = 40, j = 1; i > 20; i--, j++) {
ans *= i;
ans /= j;
}
cout << ans << endl;
return 0;
}
dp[i][j] = max(dp[i - 1][j - 1], dp[i - 1][j]) + num[i][j]
dp[i][j] = max(dp[i + 1][j + 1], dp[i + 1][j]) + num[i][j]
#include
using namespace std;
int n, num[20][20], ans[20][20];
int main(int argc, char *grav[])
{
n = 15;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= i; j++) {
cin >> num[i][j];
}
}
/* 自上而下
int fin = 0;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
ans[i][j] = max(ans[i - 1][j - 1], ans[i - 1][j]) + num[i][j];
fin = max(fin, ans[i][j]);
}
}
cout << fin << endl;
*/
// 自下而上
for (int i = n; i > 0; i--) {
for (int j = 1; j <= i; j++) {
ans[i][j] = max(ans[i + 1][j], ans[i + 1][j + 1]) + num[i][j];
}
}
cout << ans[1][1] << endl;
return 0;
}
相信大家都学过树塔问题,题目很简单求最大化一个三角形数塔从上往下走的路径和。走的规则是:(i,j) 号点只能走向 (i+1,j) 或者 (i+1,j+1)。如下图是一个数塔,映射到该数塔上行走的规则为:从左上角的点开始,向下走或向右下走直到最底层结束。
1
3 8
2 5 0
1 4 3 8
1 4 2 5 0
路径最大和是 1+8+5+4+4=22,1+8+5+3+5=22 或者 1+8+0+8+5=22。
小 S 觉得这个问题 soeasy。于是他提高了点难度,他每次 ban 掉一个点(即规定哪个点不能经过),然后询问你不走该点的最大路径和。当然他上一个询问被 ban 掉的点过一个询问会恢复(即每次他在原图的基础上 ban 掉一个点,而不是永久化的修改)。
第一行包括两个正整数 N,M 分别表示数塔的高和询问次数。
以下 N 行,第 i 行包括用空格隔开的 i−1 个数,描述一个高为 N 的数塔。
而后 M 行,每行包括两个数 X,Y,表示第 X 行第 Y 列的数塔上的点被小 S ban 掉,无法通行。
(由于读入数据较大,请使用较为快速的读入方式)
M 行每行包括一个非负整数,表示在原图的基础上 ban 掉一个点后的最大路径和,如果被 ban 掉后不存在任意一条路径,则输出 −1。
5 3
1
3 8
2 5 0
1 4 3 8
1 4 2 5 0
2 2
5 4
1 1
17
22
-1
1
3 X
2 5 0
1 4 3 8
1 4 2 5 0
1+3+5+4+4 = 17 或者 1+3+5+3+5=17
1
3 8
2 5 0
1 4 3 8
1 4 2 X 0
1+8+5+4+4 = 22
结合数塔问题:
有两种方式可以求得最长路径和,自上而下(红色)、自下而上(绿色)。
此题可以理解为是求去掉某个点,求最大的路径之和。
有自上而下和自下而上两种方式可求得经过某个点所得到的最大路径和,比如18+8-4=22,经过(4,2)的点的最大值为22.
通过此方式可以记录每行最大和次大的数记录下来,如果要去掉的位置是最大的,则次大的为结果,反之亦然。
此解法时间复杂度和空间复杂度都为O(n^2)
#include
#include
using namespace std;
int n, m, num[1005][1005], utd[1005][1005], dtu[1005][1005], ans[1005][1005], mmax[1005][2];
int main(int argc, char *grav[])
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= i; j++) {
scanf("%d", &num[i][j]);
}
}
// 从上往下求
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= i; j++) {
utd[i][j] = max(utd[i - 1][j - 1], utd[i - 1][j]) + num[i][j];
}
}
// 从下往上求
for (int i = n; i > 0; i--) {
for (int j = 1; j <= i; j++) {
dtu[i][j] = max(dtu[i + 1][j], dtu[i + 1][j + 1]) + num[i][j];
}
}
// 求经过每个点得到的最大和
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= i; j++) {
ans[i][j] = utd[i][j] + dtu[i][j] - num[i][j];
}
}
// 每行最大值的坐标和次大值
for (int i = 1; i <= n; i++) {
int m2 = 0, m1 = 0, cnt = 0;
for (int j = 1; j <= i; j++) {
if (ans[i][j] > m1) {
m2 = m1;
m1 = ans[i][j];
cnt = j;
} else if (m2 < ans[i][j]) {
m2 = ans[i][j];
}
}
// 记录下标
mmax[i][0] = cnt;
// 记录次大的数
mmax[i][1] = m2;
}
for (int i = 0; i < m; i++) {
int a, b;
scanf("%d%d", &a, &b);
if (a == 1 && b == 1) {
printf("-1\n");
} else if (mmax[a][0] == b) {
printf("%d\n", mmax[a][1]);
} else {
printf("%d\n", dtu[1][1]);
}
}
return 0;
}
先处理txt文件
字母都是大写
全局替换",“为空格” “,
方便数据读入
:%s /”,"/ /g
#include
#include
#include
using namespace std;
int main(int argc, char *grav[])
{
int n = 0;
string name[6005];
while (cin >> name[n]) {
n++;
}
sort(name, name + n);
long long ans = 0;
for (int i = 0; i < n; i++) {
int t = 0;
for (int j = 0; j < name[i].size(); j++) {
t += name[i][j] - 'A' + 1;
}
ans += t * (i + 1);
}
cout << ans << endl;
return 0;
}
cin是C++的标准输入流,其本身是一个对象,并不存在返回值的概念。
不过经常会有类似于
while(cin>>a)
的调用,这里并不是cin的返回值,应该关注">>"输入操作符,其实是它到底返回了什么
“>>”操作重载函数istream& operator>>(istream&, T&);
的返回值,其中第二个参数由cin>>后续参数类型决定。
其返回值类型为istream&类型,大多数情况下其返回值为cin本身(非0值),只有当遇到EOF输入时,返回值为0。
所以会有以下这种cin连续读取的方法cin >> x >> y;
当输入所有数据后,通过输入EOF的方法,可以退出while(cin>>a)
这样的循环。 输入EOF的方法,windows下输入ctrl+z,
Linux下输入ctrl+d。 在类似于ssize_t getline(char **lineptr, size_t *n, FILE *stream); //getline()
,在C++手册中显示Return value为input,iuput就是获取数据的流,就是getline的第一个参数
//有类似如下代码,从cin(标准输入流)中获取内容,返回值为获取内容,当遇到EOF时,返回0。
if(!getline(cin, line))
{
break;
}
#include
#include
using namespace std;
// 判断数字的位数
int digit(int x) {
// 先取对数,下取整,强转, 加1
return (int)floor(log10(x)) + 1;
}
int check(int x, int *num) {
while(x) {
if (num[x % 10] == 1) {
return 0;
}
num[x % 10] = 1;
x /= 10;
}
return 1;
}
// 判断是否全数字
int func(int a, int b, int c) {
// 标记数组,第一个数初始化为1
int num[10] = {
1};
if (check(a, num) == 0) return 0;
if (check(b, num) == 0) return 0;
if (check(c, num) == 0) return 0;
return 1;
}
int main(int argc, char *grav[])
{
int ans = 0, mark[10005] = {
0};
for (int i = 1; i < 100; i++) {
for (int j = i + 1; 1; j++) {
int a = digit(i), b = digit(j), c = digit( i * j);
// 位数等于9处理一波
if (a + b + c == 9) {
if (func(i, j, i * j)) {
// mark数组用来去重
if (mark[i * j] == 0) {
mark[i * j] = 1;
ans += i * j;
}
cout << i << " * " << j << "=" << i * j << endl;
}
} else if (a + b + c > 9) {
break;
}
}
}
cout << ans << endl;
return 0;
}
有四个非平凡的例子,求四个分数的乘积化为最简分数后的分母值 不考虑平凡解,即有0存在,不用考虑太细,可以直接要求每位都不为0
分子分母都是两位数,分母比分子大
分子:11 - 99;
分母:分子 + i
四种抹除方式 :
1、分子1 = 分母1;2、分子1 = 分母2;3、分子2 = 分母1;4、分子2 = 分母2
抹除前后判等:十字相乘
#include
using namespace std;
int check(int a, int b) {
int x1 = a / 10, x2 = a % 10;
int y1 = b / 10, y2 = b % 10;
if (!x1 || !x2 || !y1 || !y2) return 0;
if (x1 == y1 && a * y2 == b * x2) return 1;
if (x1 == y2 && a * y1 == b * x2) return 1;
if (x2 == y1 && a * y2 == b * x1) return 1;
if (x2 == y2 && a * y1 == b * x1) return 1;
return 0;
}
int gcd(int a, int b) {
if (!b) return a;
return gcd(b, a % b);
}
int main(int argc, char *grav[])
{
int a = 1, b = 1;
for (int i = 11; i < 100; i++) {
for (int j = i + 1; j < 100; j++) {
if (check(i, j)) {
a *= i;
b *= j;
cout << i << "/" << j << endl;
}
}
}
int c = gcd(a, b);
cout << b / c << endl;
return 0;
}
int num;
cin >> num;
// 00123 正常读入为 123
#include
using namespace std;
// n代表进制
int check(int x, int n) {
int raw = x, t = 0;
while (x) {
t = t * n + x % n;
x /= n;
}
return t == raw;
}
int main(int argc, char *grav[])
{
int ans = 0;
for (int i = 1; i < 1000000; i++) {
if (check(i, 10) && check(i, 2)) {
ans += i;
cout << i << endl;
}
}
cout << ans << endl;
return 0;
}
设一个X位数
其最大五次幂之和为 9^5 * X
X位数上界为 10^X
估计两个值的交点,即9^5 * X = 10^X,X大约为 5.xxx
所以X最大为 6
枚举10 ~ 1000000
⭐提前算好1 ~ 9的五次幂,存起来!
#include
using namespace std;
int num[10];
// 先求每个数的五次方
void init() {
for (int i = 1; i < 10; i++) {
int t = i;
for (int j = 1; j < 5; j++) {
t *= i;
}
num[i] = t;
}
}
int check(int x) {
int raw = x, t = 0;
while (x) {
t += num[x % 10];
x /= 10;
}
return raw == t;
}
int main(int argc, char *grav[])
{
init();
int ans = 0;
for (int i = 10; i < 1000000; i++) {
if (check(i)) {
ans += i;
cout << i << endl;
}
}
cout << ans << endl;
return 0;
}