day1
Q1 难度⭐
[NOIP2010]数字统计_牛客题霸_牛客网 (nowcoder.com)
题目:
请统计某个给定范围[L, R]的所有整数中,数字2出现的次数。 比如给定范围[2, 22],数字2在数2中出现了1次,在数12中出现1次,在数20中出现1次,在数21中出现1次,在数22中出现2次,所以数字2在该范围内一共出现 了6次。
输入描述:输入共1行,为两个正整数L和R,之间用一个空格隔开。
输出描述:输出共1行,表示数字2出现的次数。
补充说明:1≤L≤R≤10000。
思路:
递归判断每一位是否为2,并计数。
#include <iostream>
using namespace std;
int check(int n)
{
int r = 0;
if(n < 10)
{
if(n == 2) r++;
}
else
{
if( n % 10 == 2) r++;
n /= 10;
r += check(n);
}
return r;
}
int main()
{
int a, b;
int res = 0;
cin >> a >> b;
int n = b - a + 1;
int m[n];
for(int q = 0; q < n; q ++)
{
m[q] = a + q;
if(check(m[q]) != 0 ) res += check(m[q]);
}
printf("%d", res);
return 0;
}
Q2 难度⭐
两个数组的交集_牛客题霸_牛客网 (nowcoder.com)
题目:
给定两个整数数组分别为nums1,nums2,找到它们的公共元素并按返回。
数据范围:
1<nums1.length,nums2.length< 1000
1<nums1[i],nums2[i]<1000
思路:
先排序,再双指针算法遍历寻找公共元素并判重。
#include <vector>
class Solution
{
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2)
{
sort(nums1.begin(), nums1.end());
sort(nums2.begin(), nums2.end());
vector<int> r;
int i = 0;
int j = 0;
while (i < nums1.size() && j < nums2.size())
{
if (nums1[i] == nums2[j])
{
if (r.empty() || r.back() != nums2[j]) r.push_back(nums1[i]);
i++;
j++;
}
else if (nums1[i] < nums2[j])
i++;
else
j++;
}
return r;
}
};
Q3 难度⭐⭐
题目:
牛牛拿到了一个字符串。 他每次“点击”,可以把字符串中相邻两个相同字母消除,例如,字符串 " abbc " 点击后可以生成 " ac " 。 但相同而不相邻、不相同的相邻字母都是不可以被消除的。 牛牛想把字符串变得尽可能短。他想知道,当他点击了足够多次之后,字符串的最终形态是什么?
输入描述:一个字符串,仅由小写字母组成。(字符串长度不大于300000)
输出描述:一个字符串,为“点击消除”后的最终形态。若最终的字符串为空串,则输出0。
思路:
新建一个栈,通过入栈与判断后续字符与栈顶元素是否相等,从而进行入栈与出栈,最后形成无相同元素相邻的栈。
#include <iostream>
#include <stack>
#include <string>
using namespace std;
int main()
{
string n;
cin >> n;
int i = 0;
bool flag;
stack<char> s;
s.push(n[0]);
for(int i = 1; i < n.size(); i++)
{
if(!s.empty() && n[i] == s.top())
s.pop();
else
s.push(n[i]);
}
string res = "";
while(!s.empty())
{
res = s.top() + res;
s.pop();
}
if(!res.empty())
cout << res << endl;
else
cout << 0 <<endl;
return 0;
}
day2
Q1 难度⭐
题目:
牛牛正在寄快递,他了解到快递在 1kg 以内的按起步价 20 元计算,超出部分按每 kg 1元计算,不足 1kg 部分按 1kg计算。如果加急的话要额外付五元,请问 牛牛总共要支付多少快递费
输入描述:第一行输入一个单精度浮点数 a 和一个字符 b ,a 表示牛牛要寄的快递的重量,b表示牛牛是否选择加急,'y' 表示加急 ,'n' 表示不加急。
输出描述:输出牛牛总共要支付的快递费用。
思路:
分块计算
- 是否满足1kg
- 是否有小数
- 是否加急
#include <iostream>
using namespace std;
int main()
{
float a;
char b;
cin >> a >> b;
int x = (int) a;
int ans = 0;
if(x == 0 && x < a)
ans += 20;
if(x >= 1)
{
ans += x + 19 ;
if(x < a) ans +=1;
}
if(b == 'y') ans += 5;
cout << ans << endl;
return 0;
}
Q2 难度⭐⭐
最小花费爬楼梯_牛客题霸_牛客网 (nowcoder.com)
题目:
给定一个整数数组cost,其中cost[i]是从楼梯第个i台阶向上爬需要支付的费用,下标从0开始。一旦你支付此费用,即可选择向上爬一个或者两个台 阶。 你可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯。 请你计算并返回达到楼梯顶部的最低花费。
输入描述:第一行输入一个正整数 n ,表示数组 cost 的长度。 第二行输入 n 个正整数,表示数组 cost 的值。
输出描述:输出最低花费
思路:
dp
状态转移方程为:min_cost[i] = min(cost[i-1] + min_cost[i-1], cost[i-2] + min_cost[i-2])
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main()
{
int n;
scanf("%d", &n);
vector<int> cost(n);
for (int i = 0; i < n; i++)
scanf("%d", &cost[i]);
if(n < 3) cout << 0;
vector<int> min_cost(n+1, 0);
for (int i = 2; i <= n; i++)
min_cost[i] = min(cost[i-1] + min_cost[i-1], cost[i-2] + min_cost[i-2]);
printf("%d", min_cost[n]);
return 0;
}
Q3 难度⭐⭐
数组中两个字符串的最小距离_牛客题霸_牛客网 (nowcoder.com)
题目:
给定给定两个字符串str1和str2,再一个字符串数组strs,返回在strs中str1和str2的最小距离,如果str1或str2为null,或不在strs中,返回-1
输入描述:输入包含有多行,第一输入一个整数n(),代表数组strs的长度,第二行有两个字符串分别代表str1和str2,接下来n行,每行一个字符 串,代表数组strs (保证题目中出现的所有字符串长度均小于等于10)。
输出描述:输出一行,包含一个整数,代表返回的值。
思路:
顺序查找两个字符串,并双指针更新最短距离
#include <iostream>
#include <string>
using namespace std;
int main()
{
int n;
cin >> n;
string s1, s2, ss;
cin >> s1 >> s2;
int l = -1, r = -1;
int m = 0x3f3f3f3f;
for (int i = 0; i < n; i++)
{
cin >> ss;
if (ss == s1)
{
l = i;
if (r != -1)
m = min( m, abs(r - l));
}
else if (ss == s2)
{
r = i;
if (l != -1)
m = min( m, abs(r - l));
}
}
if (l == -1 || r == -1)
cout << -1 << endl;
else
cout << m;
return 0;
}
day3
Q1 难度⭐
题目:
规定一种对于复合词的简写方式为只保留每个组成单词的首字母,并将首字母大写后再连接在一起
比如 “College English Test”可以简写成“CET”,“Computer Science”可以简写为“CS”,“I am Bob”简写为“IAB”
输入一个长复合词(组成单词数sums, sums≥1且sums≤100,每个单词长度len, len≥1且len≤50),请你输出它的简写
思路:
每次输入前更新判断上一个输入的是否为‘ ’
#include <iostream>
using namespace std;
int main()
{
char input = 0, prev = 0, ret[100] = { 0 };
int a = 0;
while (input != '\n')
{
//cin >> input;
scanf("%c", &input);
if (a == 0 || prev == ' ')
{
if (input >= 'a' && input <= 'z')
input -= 32;
ret[a] = input;
cout << ret[a];
a++;
}
prev = input;
}
return 0;
}
Q2 难度⭐⭐
题目:
读入n,x给出n个数a[1],a[2],……,a[n],求最小的区间[l,r],使a[l]+a[l+1]+……+a[r]≥x,若存在相同长度区间,输出l最小的那个
输入描述:第一行两个数,n(1≤n≤10000000),x(1≤x≤10000) 第二行n个数a[i](1≤a[i]≤1000)
输出描述:输出符合条件l,r(保证有解)
思路:
双指针i,j更新区间,o,p更新最短的i,j区间
#include <iostream>
using namespace std;
int main()
{
int n, x;
cin >> n >> x;
int a[n + 1];
int sum = 0;
int o = 0, p = 0;
int q = 0x3f3f3f3f, j = 1;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
sum += a[i];
while (sum >= x)
{
if (i - j < q)
{
q = i - j;
o = j;
p = i;
}
sum -= a[j++];
}
}
cout << o << ' ' << p << endl;
return 0;
}
Q3 难度⭐⭐⭐
题目:
给一个数组,一共有 n 个数。
你能进行最多 k 次操作。每次操作可以进行以下步骤:
- 选择数组中的一个偶数 ai,将其变成 ai/2 。
现在你进行不超过 k 次操作后,让数组中所有数之和尽可能小。请输出这个最小的和。
输入描述:第一行输入两个正整数 n 和 k ,用空格隔开 第二行输入n 个正整数 ai
数据范围: , , 。
输出描述:一个正整数,代表和的最小值。
思路:
最多k次将最大的偶数/2,且值的大小顺序更新存储,考虑使用priority_queue。
#include <iostream>
#include <queue>
using namespace std;
priority_queue<int> q;
int main()
{
int n, k;
cin >> n >> k;
long long ans = 0;
for (int i = 1; i <= n; i++)
{
int a;
cin >> a;
if (a % 2 == 0)
q.push(a);
else
ans += a;
}
while (k > 0 && q.size())
{
int x = q.top();
q.pop();
x /= 2;
k--;
if (x % 2 == 0)
q.push(x);
else
ans += x;
}
while (q.size())
ans += q.top(), q.pop();
cout << ans << endl;
return 0;
}
day4
Q1 难度⭐
Fibonacci数列_牛客题霸_牛客网 (nowcoder.com)
题目:
Fibonacci数列是这样定义的:
F[0] = 0
F[1] = 1
for each i ≥ 2: F[i] = F[i-1] + F[i-2]
因此,Fibonacci数列就形如:0, 1, 1, 2, 3, 5, 8, 13, ...,在Fibonacci数列中的数我们称为Fibonacci数。给你一个N,你想让其变为一个Fibonacci数,每一步你可以把当前数字X变为X-1或者X+1,现在给你一个数N求最少需要多少步可以变为Fibonacci数。
输入描述:输入为一个正整数N(1 ≤ N ≤ 1,000,000)
输出描述:输出一个最小的步数变为Fibonacci数"
思路:
求出比N大的第一个斐波那契数,再判断与前后两个数的距离
#include <iostream>
using namespace std;
int main()
{
int n;
cin >> n;
int a = 0, b = 1, c = a + b;
while(1)
{
a = b;
b = c;
c = a + b;
if(n <= c) break;
}
int ans = n - b < c - n ? n - b : c - n;
cout << ans;
return 0;
}
Q2 难度⭐⭐⭐
题目:
给出一个二维字符数组和一个单词,判断单词是否在数组中出现,单词由相邻单元格的字母连接而成,相邻单元指的是上下左右相邻。同一单元格的字母不能多次使用。
数据范围:
0 < 行长度 <= 100
0 < 列长度 <= 100
0 < 单词长度 <= 1000
思路:
多源DFS
class Solution
{
public:
bool exist(vector<string> &board, string word)
{
int m = board.size();
int n = board[0].size();
for(int i = 0; i < m; i++)
for(int j = 0; j < n; j++)
if(dfs(board, word, i, j, 0))
return true;
return false;
}
bool dfs(vector<string> &board, string& word, int x, int y, int ans)
{
if(ans == word.size()) return true;
if(x < 0 || x >= board.size() || y < 0 || y >= board[0].size() || word[ans] != board[x][y]) return false;
char temp = board[x][y];
board[x][y] = '\0';
bool res = dfs(board, word, x+1, y, ans+1) ||
dfs(board, word, x-1, y, ans+1) ||
dfs(board, word, x, y+1, ans+1) ||
dfs(board, word, x, y-1, ans+1);
board[x][y] = temp;
return res;
}
};
Q3 难度⭐⭐
题目:
KiKi知道什么叫杨辉三角之后对杨辉三角产生了浓厚的兴趣,他想知道杨辉三角的前n行,请编程帮他解答。杨辉三角,本质上是二项式(a+b)的n次方展开后各项的系数排成的三角形。其性质包括:每行的端点数为1, 一个数也为1;每个数等于它左上方和上方的两数之和。
输入描述:第一行包含一个整数数n。 (1≤n≤30)
输出描述:包含n行,为杨辉三角的前n行,每个数输出域宽为5。
思路:
杨辉三角,,,,,注意“输出域宽为5”--->“%5d”
#include <iostream>
using namespace std;
const int N = 100;
int yh[N][N];
int main()
{
int n, i, j;
scanf("%d", &n);
for(i=0; i<n; i++)
{
for(j=0; j<=i; j++)
{
if( j == 0 || i == j)
yh[i][j] = 1;
else
yh[i][j] = yh[i-1][j-1] + yh[i-1][j];
printf("%5d", yh[i][j]);
}
printf("\n");
}
return 0;
}
day5
Q1 难度⭐
题目:
游游现在有a个'y',b个'o',c个'u',他想用这些字母拼成一个字符串。三个相邻的字母是"you"可以获得2分,两个相邻的字母是"oo",可以获得1分。问最多可以获得多少分?
输入描述:第一行一个整数q,代表询问次数。接下来q行,每行三个正整数a,b,c,用空格隔开。
,
输出描述:输出q行,代表每次询问的答案。
思路:
最多只能组成abc中最小值个’you',计算剩下的'o'可拼成最多的'oo'
#include <iostream>
using namespace std;
int main()
{
int q;
int a, b, c;
int res1, res2, res;
cin >> q;
while(q--)
{
scanf("%d %d %d", &a, &b, &c);
int k = min(min(a, b), c);
res1 = 2 * k;
res2 = max(b - k - 1, 0);
res = res1 + res2;
printf("%d\n", res);
}
return 0;
}
Q2 难度⭐⭐⭐
题目:
给定一个 n×m 的网格,其中每个单元格中可能有三种值中的一个 0 , 1 , 2。其中 0 表示这个格子为空、1 表示这个格子有一个完好的苹果,2 表示这个格子有一个腐烂的苹果。腐烂的苹果每分钟会向上下左右四个方向的苹果传播一次病菌,并导致相邻的苹果腐烂。请问经过多少分钟,网格中不存在完好的苹果。如果有苹果永远不会腐烂则返回 -1
数据范围:1≤n,m≤1000 ,网格中的值满足 0≤val≤2
思路:
多源BFS,并更新状态
class Solution
{
public:
// 定义一个二维数组 rot,其中每个元素是一个包含两个整数的数组,表示旋转的方向。
int rot[4][2] = {{-1,0},{1,0},{0,1},{0,-1}};
// 定义变量 f 和 min,记录新鲜苹果数量和腐烂时间。
int f = 0, min = 0;
// 定义一个队列 que,用于存储待处理的苹果位置。
queue<pair<int, int>> que;
int rotApple(vector<vector<int> >& grid)
{
// 遍历网格的每个位置。
for(int i = 0; i < grid.size(); i++)
{
for(int j = 0; j < grid[0].size(); j++)
{
// 如果发现腐烂的苹果,增加 f 的值。
if(grid[i][j] == 1) f++;
// 如果发现腐烂的苹果,将其添加到队列中。
else if (grid[i][j] == 2) que.push({i,j});
}
}
while(!que.empty())
{
// 获取队列中的苹果数量。
int n = que.size();
// 定义一个布尔变量 rotten,用于记录是否有苹果腐烂。
bool rotten = false;
// 遍历队列中的每个苹果。
for(int i = 0; i < n ; i++)
{
auto x = que.front();
que.pop();
// 遍历旋转的方向。
for(auto cur : rot)
{
// 计算旋转后的位置。
int i = x.first + cur[0];
int j = x.second + cur[1];
// 如果旋转后的位置在网格范围内且为新鲜苹果,则将该苹果腐烂。
if(i >= 0 && i < grid.size() && j >= 0 && j < grid[0].size() && grid[i][j] == 1)
{
grid[i][j] = 2;
// 将腐烂的苹果添加到队列中,以便继续处理。
que.push({i,j});
// 减少新鲜苹果的数量。
f--;
// 标记有苹果腐烂。
rotten = true;
}
}
}
// 如果有苹果腐烂,增加腐烂时间。
if(rotten) min++;
}
// 如果还有新鲜苹果,返回 -1,否则返回腐烂时间。
return f ? -1 : min;
}
};
Q3 难度⭐⭐
孩子们的游戏(圆圈中最后剩下的数)_牛客题霸_牛客网 (nowcoder.com)
题目:
每年六一儿童节,牛客都会准备一些小礼物和小游戏去看望孤儿院的孩子们。其中,有个游戏是这样的:首先,让 n 个小朋友们围成一个大圈,小朋友们的编号是0~n-1。然后,随机指定一个数 m ,让编号为0的小朋友开始报数。每次喊到 m-1 的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0... m-1报数....这样下去....直到剩下最后一个小朋友,可以不用表演,并且拿到牛客礼品,请你试着想下,哪个小朋友会得到这份礼品呢?、
思路:
class Solution
{
public:
int LastRemaining_Solution(int n, int m)
{
if(n == 0) return -1;
int j = 0;
for(int i = 2; i <= n; i ++)
j = (j + m ) % i;
return j;
}
};
day6
Q1 难度⭐⭐
题目:
以字符串的形式读入两个数字,编写一个函数计算它们的和,以字符串形式返回。
数据范围:s.length,t.length≤100000,字符串仅由'0'~‘9’构成
要求:时间复杂度 O(n)
思路:
记录每一位数的和的个位数与进位,拼接每一位
class Solution
{
public:
// solve 函数,计算s 和 t 的加法结果
string solve(string s, string t)
{
// 计算两个字符串的长度,并减去 1,因为字符串的索引是从 0 开始的。
int n = s.size() - 1, m = t.size() - 1;
// 初始化一个空字符串 sum,用于存放最终的加法结果。
string sum = "";
// 初始化进位 r 为 0。
int r = 0;
// 当任何一个字符串的索引大于等于 0 或进位 r 大于 0 时,执行循环。
while(n >= 0 || m >= 0 || r > 0) {
// 初始化两个变量 a 和 b,存储当前要相加的数字。
int a = 0, b = 0;
if(n >= 0)
{
a = s[n] - '0';
n--;
}
if(m >= 0)
{
b = t[m] - '0';
m--;
}
// 计算 a、b 和进位 r 的和,并赋值给 ans。
int ans = a + b + r;
// 判断是否需要进位
if(ans >= 10)
{
ans -= 10;
r = 1;
}
else r = 0;
// 将 ans 转换为字符串,并将其添加到 sum 的开头。
sum = to_string(ans) + sum;
}
return sum;
}
};
Q2 难度⭐⭐⭐
链表相加(二)_牛客题霸_牛客网 (nowcoder.com)
题目:
假设链表中每一个节点的值都在 0 - 9 之间,那么链表整体就可以代表一个整数。给定两个这种链表,请生成代表两个整数相加值的结果链表。
数据范围:0≤n,m≤1000000,链表任意值 0≤val≤9
要求:空间复杂度 O(n),时间复杂度 O(n)
思路:
先翻转链表,创建新链表记录每一位数的和的个位数与进位,再反转
class Solution
{
public:
// ReLt 函数,逆置链表
ListNode* ReLt(ListNode* head)
{
if(head == NULL || head -> next == NULL) return head;
else
{
ListNode* newhead = ReLt(head -> next);
head ->next ->next = head;
head ->next = NULL;
return newhead;
}
}
// addInList 函数,计算两个逆置后的链表的和
ListNode* addInList(ListNode* head1, ListNode* head2)
{
if(head1 == NULL) return head2;
if(head2 == NULL) return head1;
// 逆置两个链表
head1 = ReLt(head1);
head2 = ReLt(head2);
ListNode* ans = new ListNode(0);
ListNode* cur = ans;
int r = 0; // 用于存储进位
// 当两个链表之一或进位不为空时,执行循环
while (head1 || head2 || r)
{
int val1 = head1 ? head1->val : 0;
int val2 = head2 ? head2->val : 0;
int sum = val1 + val2 + r;
r = sum / 10;
sum %= 10;
// 创建新的节点并加入到结果链表中
cur->next = new ListNode(sum);
cur = cur->next;
// 移动链表指针
if (head1) head1 = head1->next;
if (head2) head2 = head2->next;
}
// 返回逆置后的结果链表
return ReLt(ans->next);
}
};
Q3 难度⭐⭐⭐
题目:
以字符串的形式读入两个数字,编写一个函数计算它们的乘积,以字符串形式返回。
数据范围: 读入的数字大小满足
要求:空间复杂度 ,时间复杂度 (假设m是n的长度)
思路:
记录s的每一位乘t的每一位的得数的个位数与进位,再累加
class Solution
{
public:
// 该函数接收两个字符串 s 和 t,返回它们的乘积。
string solve(string s, string t)
{
// 任何数乘以 "0" 都是 "0"。
if(s == "0" || t == "0") return "0";
// 获取两个字符串的长度。
int n = s.size(), m = t.size();
// 初始化一个空字符串 ans,用于存放最终结果。
string ans;
// 创建一个整数数组 res,用于存放每一步的乘积和进位。
vector<int> res(n + m);
// 从后往前遍历字符串 s 和 t。
for(int i = n - 1; i >= 0; i--)
{
int r = 0; // 初始化进位为 0。
for(int j = m - 1; j >= 0; j--)
{
// 计算当前位的乘积,并加上前一位的进位。
int num = (s[i] - '0') * (t[j] - '0') + r + res[i + j + 1];
// 更新当前位的乘积结果,取个位数。
res[i + j + 1] = num % 10;
// 更新进位。
r = num / 10;
}
// 将当前行的进位加到下一行。
res[i] += r;
}
// 跳过所有前导0
int i = 0;
while(i < n + m && res[i] == 0) i++;
// 将结果拼成字符串。
while (i < n + m)
ans.push_back(res[i++] + '0');
return ans;
}
};