day1
Q1 难度⭐⭐
题目 :
字符串旋转:
给定两字符串A和B,如果能将A从中间某个位置分割为左右两部分字符串(可以为空串),并将左边的字符串移动到右边字符串后面组成新的字符串可以变为字符串B时返回true。
例如:如果A='youzan',B='zanyou',A按'you''zan'切割换位后得到'zanyou'和B相同,返回true。
再如:如果A='abcd',B='abcd',A切成'abcd'和''(空串),换位后可以得到B,返回true。
数据范围:A,B字符串长度满足 𝑛≤1000,保证字符串中仅包含小写英文字母和阿拉伯数字
思路:
先判断AB长度和内容是否相同,再拼接AA在其中找B
cpp
class Solution
{
public:
bool solve(string A, string B)
{
if(A == B)
return true;
if(A.size() != B.size())
return false;
A += A;
if(A.find(B) != -1)
return true;
else
return false;
}
};
Q2 难度⭐⭐⭐
合并k个已排序的链表_牛客题霸_牛客网 (nowcoder.com)
题目 :
合并 k 个升序的链表并将结果作为一个升序的链表返回其头节点。
思路:
先将所有数据放在同一个数组并排序,再创建链表并放入数据
cpp
class Solution
{
public:
ListNode* mergeKLists(vector<ListNode*>& lists)
{
vector<int> a;
for(int i = 0; i < lists.size(); i ++)
while(lists[i])
{
a.push_back(lists[i] -> val);
lists[i] = lists[i] -> next;
}
sort(a.begin(), a.end());
ListNode* h = new ListNode(0);
ListNode* ans = h;
for(auto x : a)
{
h -> next = new ListNode(x);
h = h -> next;
}
return ans -> next;
}
};
Q3 难度⭐⭐⭐⭐
题目 :
给定一个𝑛×𝑚的矩阵,矩阵中的数字表示滑雪场各个区域的高度,你可以选择从任意一个区域出发,并滑向任意一个周边的高度严格更低的区域(周边的定义是上下左右相邻的区域)。请问整个滑雪场中最长的滑道有多长?(滑道的定义是从一个点出发的一条高度递减的路线)
输入描述:第一行输入两个正整数 n 和 m 表示矩阵的长宽。
后续 n 行输入中每行有 m 个正整数,表示矩阵的各个元素大小。
输出描述:输出最长递减路线。
思路:
记忆化搜索动态规划问题-CSDN博客
状态转移方程为:v = max(v, dp(a,b) + 1)
cpp
#include<iostream>
#include<cstring>
using namespace std;
const int N = 110;
int n, m;
int h[N][N];
int f[N][N];
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
int dp(int x, int y)
{
int& v = f[x][y];
if (v != -1) return v; // 记忆化搜索核心
v = 1;
for (int i = 0; i < 4; i++)
{
int a = x + dx[i], b = y + dy[i];
if (a >= 1 && a <= n && b >= 1 && b <= m && h[a][b] < h[x][y]) // 判断是否越界且上一个经过的点的高度是否大于当前高度
v = max(v, dp(a, b) + 1);
}
return v;
}
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
scanf("%d", &h[i][j]);
memset(f, -1, sizeof f);
int res = 0;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
res = max(res, dp(i, j));
printf("%d\n", res);
return 0;
}
day2
Q1 难度⭐⭐
题目 :
有n个果冻排成一排。第i个果冻的美味度是 ai;天使非常喜欢吃果冻,但她想把最好吃的果冻留到最后收藏。天使想知道前x个果冻中,美味度第二大的果冻有多少美味度?
一共有 q 次询问。
注:如果最大的数有两个以上,默认第二大的等于最大的。例如,[2,3,4,2,4]这个序列,第二大的数是4。
输入描述:第一行一个正整数n。
第二行n个正整数ai,用空格隔开。
第三行一个正整数q。
接下来的 q行,每行一个正整数,代表一次询问。
输出描述:输出q行,每行一个正整数,代表一次询问,输出前x个果冻中美味度第二大的值。
思路:
priority_queue插入,记录根,删根,取根(ans数组记录),再插入记录的根
cpp
#include <iostream>
#include <queue>
using namespace std;
int ans[100005] = {0};
int main()
{
int n, x;
priority_queue<int> a;
a.push(0);
cin >> n;
for(int i = 1; i <= n; i ++)
{
cin >> x;
a.push(x);
x = a.top();
a.pop();
ans[i] = a.top();
a.push(x);
}
int q;
cin >> q;
while(q--)
{
cin >> x;
cout << ans[x] << endl;
}
return 0;
}
Q2 难度⭐⭐⭐
E-dd爱旋转_牛客小白月赛34 (nowcoder.com)
题目 :
读入一个n∗n的矩阵,对于一个矩阵有以下两种操作
1:顺时针旋180°180°180°
2:关于行镜像
给出𝑞个操作,输出操作完的矩阵
输入描述:第一行一个数n(1≤n≤1000),表示矩阵大小
接下来n行,每行n个数,描述矩阵,其中数字范围为[1,2000]
接下来一行一个数q(1≤q≤100000),表示询问次数
接下来q行,每行一个数x(x=1或x=2),描述每次询问
输出描述:n行,每行n个数,描述操作后的矩阵
思路:
记录旋转和镜像各自的总次数
若镜像次数为偶数,则结果不变,为奇数则交换对应行
若旋转次数为偶数,则结果不变,为奇数则倒叙输出即可
cpp
#include <iostream>
using namespace std;
int a[1010][1010];
int n, q, x, revolve, mirroring;
int main()
{
cin >> n;
for(int i = 1; i <= n; i ++)
for(int j = 1; j <= n; j ++)
cin >> a[i][j];
cin >> q;
while(q --)
{
cin >> x;
if(x == 1)
revolve ++;
else
mirroring ++;
}
if(mirroring % 2)
{
for(int i = 1; i <= (n / 2); i ++)
for(int j = 1; j <= n; j ++)
swap(a[i][j], a[n - i + 1][j]);
}
if(revolve % 2)
{
for(int i = n; i >= 1; i --)
{
for(int j = n; j >= 1; j --)
cout << a[i][j] << " ";
cout << endl;
}
}
else
{
for(int i = 1; i <= n; i ++)
{
for(int j = 1; j <= n; j ++)
cout << a[i][j] << " ";
cout << endl;
}
}
return 0;
}
Q3 难度⭐⭐⭐⭐
题目 :
小红拿到了一个数组,她想取一些数使得取的数之和尽可能大,但要求这个和必须是 𝑘 的倍数。
你能帮帮她吗?
输入描述:第一行输入两个正整数 𝑛 和 𝑘
第二行输入 𝑛 个正整数 𝑎𝑖
输出描述:如果没有合法方案,输出 -1。否则输出最大的和。
思路:
DP
状态转移方程为:dp[i][(j + arr[i]) % k] = max(dp[i - 1][j] + arr[i], dp[i - 1][(j + arr[i]) % k]);
cpp
#include <iostream>
#include <vector>
#include <climits>
using namespace std;
int main()
{
int n, k;
cin >> n >> k;
vector<long long> arr(n + 1, 0);
for (int i = 1; i <= n; ++i)
cin >> arr[i];
vector<vector<long long>> dp(n + 1, vector<long long>(k, LLONG_MIN));
dp[0][0] = 0;
for (int i = 1; i <= n; ++i)
for (int j = 0; j < k; ++j)
dp[i][(j + arr[i]) % k] = max(dp[i - 1][j] + arr[i], dp[i - 1][(j + arr[i]) % k]);
if (dp[n][0] <= 0)
cout << -1 << endl;
else
cout << dp[n][0] << endl;
return 0;
}
day3
Q1 难度⭐
H-神奇的字母(二)_牛客小白月赛25 (nowcoder.com)
题目 :
我有个神奇的字母。
但我不告诉你是什么字母。
什么?你想知道这个字母是什么?那你就来猜呀~
字母的范围从'a'到'z'。
我会给你一段话,神奇的字母就是出现次数最多的那个字母哦~
输入描述:一段话,仅由英文小写字母和空格组成。这段话可能有很多行。
输出描述:出现次数最多的那个神奇的字母。
思路:
将字母映射到数组中并计数
cpp
#include <iostream>
using namespace std;
int main()
{
char x;
int a[123] = {0};
while(cin >> x)
{
if(x >= 'a' && x <= 'z')
a[x] ++;
}
int ans = 0;
for(char i = 'a'; i <= 'z'; i ++)
{
if(a[i] > ans)
{
ans = a[i];
x = i;
}
}
cout << x << endl;
return 0;
}
Q2 难度⭐⭐
题目 :
请设计一个算法,给一个字符串进行二进制编码,使得编码后字符串的长度最短。
数据范围:字符串长度满足 1<𝑛≤1000,本题有多组输入
输入描述:每组数据一行,为待编码的字符串。保证字符串长度小于等于1000。
输出描述:一行输出最短的编码后长度。
思路:
哈夫曼编码
cpp
#include <iostream>
#include <vector>
#include <string>
#include <queue>
using namespace std;
int main()
{
string s;
while(cin >> s)
{
int hash[256] = {0};
for(auto ss : s)
hash[ss] ++;
priority_queue<int, vector<int>, greater<int> > h;
for(int i = 0; i < 256; i ++)
if(hash[i])
h.push(hash[i]);
int ans = 0;
while(h.size() > 1)
{
int a = h.top(); h.pop();
int b = h.top(); h.pop();
ans += a + b;
h.push(a + b);
}
cout << ans << endl;
}
return 0;
}
Q3 难度⭐⭐⭐
最少的完全平方数_牛客题霸_牛客网 (nowcoder.com)
题目 :
给定一个正整数n,请找出最少个数的完全平方数,使得这些完全平方数的和等于n。完全平方指用一个整数乘以自己例如1*1,2*2,3*3等,依此类推。若一个数能表示成某个整数的平方的形式,则称这个数为完全平方数。例如:1,4,9,和16都是完全平方数,但是2,3,5,8,11等等不是数据范围:1≤𝑛≤10^4
输入描述:仅一行,输入一个正整数 n
输出描述:按题目要求输出完全平方数之和为n的最少个数
思路:
完全背包
cpp
#include <iostream>
#include <cstring>
using namespace std;
const int N = 1e4 + 5;
int n;
int dp[N];
int main()
{
cin >> n;
memset(dp, 0x3f3f3f, sizeof dp);
dp[0] = 0;
for(int i = 1; i * i <= n; i ++)
for(int j = i * i; j <= n; j ++)
dp[j] = min(dp[j], dp[j - i * i] + 1);
cout << dp[n] << endl;
return 0;
}
day4
Q1 难度⭐⭐
B-游游的字母串_牛客周赛 Round 3 (nowcoder.com)
题目 :
对于一个小写字母而言,游游可以通过一次操作把这个字母变成相邻的字母。'a'和'b'相邻,'b'和'c'相邻,以此类推。特殊的,'a'和'z'也是相邻的。可以认为,小写字母的相邻规则为一个环。
游游拿到了一个仅包含小写字母的字符串,她想知道,使得所有字母都相等至少要多少次操作?
输入描述:一个仅包含小写字母,长度不超过100000的字符串。
输出描述:一个整数,代表最小的操作次数。
思路:
遍历a到z,寻找最少的操作次数
cpp
#include <iostream>
#include <cmath>
using namespace std;
int main()
{
string s;
cin >> s;
int ans = 0x3f3f3f;
for(char i = 'a'; i <= 'z'; i ++)
{
int sum = 0;
for(int j = 0; j < s.size(); j ++)
sum += min(abs(s[j] - i), 26 - abs(s[j] - i));
ans = min(sum, ans);
}
cout << ans << endl;
return 0;
}
Q2 难度⭐⭐⭐⭐
体育课测验(二)_牛客题霸_牛客网 (nowcoder.com)
题目 :
体育课共有numProject个考核项目,编号为0到numProject-1,考核中每两个项
目被划分为一组得到分组数组groupsi,现规定若想完成项目groupsi[0],必须先完成
groupsi[1]。保证所有分组互不相同,若分组情况能顺利完成考核,请返回任意的一个完
成顺序,否则返回空数组。
思路:
拓扑排序 拓扑排序-CSDN博客
cpp
class Solution
{
public:
vector<int> findOrder(int numProject, vector<vector<int>>& groups)
{
// 初始化邻接表 h, e, ne 和入度数组 d
vector<int> h(numProject, -1), e, ne, d(numProject, 0);
int idx = 0; // 边的索引
// 建立邻接表和入度表
for (auto group : groups)
{
// group[0] 依赖于 group[1],因此从 group[1] 到 group[0] 有一个有向边
e.push_back(group[0]);
ne.push_back(h[group[1]]);
h[group[1]] = idx++;
d[group[0]]++; // group[0] 的入度加1
}
// 调用拓扑排序函数
vector<int> q(numProject);
if (!topsort(numProject, h, e, ne, d, q))
return {}; // 如果不存在拓扑排序,返回空数组
// 从队列中提取完成顺序
vector<int> result;
for (int i = 0; i < numProject; ++i)
result.push_back(q[i]);
return result;
}
private:
bool topsort(int n, vector<int>& h, vector<int>& e, vector<int>& ne, vector<int>& d, vector<int>& q)
{
int hh = 0, tt = -1;
// 将所有入度为0的节点加入队列
for (int i = 0; i < n; ++i)
if (!d[i])
q[++tt] = i;
while (hh <= tt)
{
int t = q[hh++];
for (int i = h[t]; i != -1; i = ne[i])
{
int j = e[i];
if (--d[j] == 0)
q[++tt] = j;
}
}
// 如果所有点都入队了,说明存在拓扑序列;否则不存在拓扑序列。
return tt == n - 1;
}
};
Q3 难度⭐⭐⭐
题目 :
N位同学站成一排,音乐老师要请其中的(N-K)位同学出列,使得剩下的K位同学不交换位置就能排成合唱队形。 合唱队形是指这样的一种队形:设K位同学从左到右依次编号为1, 2, ..., K,他们的身高分别为T1, T2, ..., TK, 则他们的身高满足T1 < T2 < ... < Ti , Ti > Ti+1 > ... > TK (1 <= i <= K)。 你的任务是,已知所有N位同学的身高,计算最少需要几位同学出列,可以使得剩下的同学排成合唱队形。
输入描述:第一行输入一个正整数 n 表示同学的总数。
第二行有 n 个整数,用空格分隔,第 i 个整数 ti 是第 i 位同学的身高(厘米)。
输出描述:输出仅有一个整数,即最少需要几个同学出列
思路:
分别求出每个位置从左往右与从右往左的最长上升子序列并合并
cpp
#include <iostream>
using namespace std;
int main()
{
int n, len = 0;
cin >> n;
int arr[n], dpl[n], dpr[n];
for (int i = 0; i < n; i ++)
{
cin >> arr[i];
dpl[i] = 1;
dpr[i] = 1;
}
//从左往右最长上升子序列
for (int i = 1; i < n; i ++)
for (int j = 0; j < i; j ++)
if (arr[j] < arr[i]) dpl[i] = max(dpl[j] + 1, dpl[i]);
//从右往左最长上升子序列
for (int i = n - 2; i >= 0; i --)
for (int j = n - 1; j > i; j --)
if (arr[i] > arr[j]) dpr[i] = max(dpr[j] + 1, dpr[i]);
//合并左右
for (int i = 0; i < n; i ++)
len = max(len, dpl[i] + dpr[i] - 1);
cout << n - len;
return 0;
}
day5
Q1 难度⭐⭐
题目 :
在 4x4 的棋盘上摆满了黑白棋子,黑白两色棋子的位置和数目随机,其中0代表白色,1代表黑色;左上角坐标为 (1,1) ,右下角坐标为 (4,4) 。
现在依次有一些翻转操作,要对以给定翻转坐标(x,y)(也即第x行第y列)为中心的上下左右四个棋子的颜色进行翻转。
给定两个数组 A 和 f ,分别代表 初始棋盘 和 哪些要进行翻转的位置(x,y) ,请返回经过所有翻转操作后的棋盘。
思路:
自身不变,上下左右翻转(注意判断边界)
cpp
#include <vector>
class Solution
{
public:
vector<vector<int> > flipChess(vector<vector<int> >& A, vector<vector<int> >& f)
{
for(int i = 0; i < f.size(); i ++)
{
int x = f[i][0] - 1;
int y = f[i][1] - 1;
flips(A, x, y);
}
return A;
}
private:
void flips(vector<vector<int> >& A, int x, int y)
{
if(x-1 >=0 && y>=0 && x-1<4 && y<4)
A[x-1][y] = !A[x-1][y];
if(x+1 >=0 && y>=0 && x+1<4 && y<4)
A[x+1][y] = !A[x+1][y];
if(x>=0 && y-1>=0 && x<4 && y-1<4)
A[x][y-1] = !A[x][y-1];
if(x>=0 && y+1>=0 && x<4 && y+1<4)
A[x][y+1] = !A[x][y+1];
}
};
Q2 难度⭐⭐
题目 :
露米娅作为宵暗的妖怪,非常喜欢吞噬黑暗。这天,她来到了一条路上,准备吞噬这条路上的黑暗。这条道路一共被分为n部分,每个部分上的黑暗数量为ai。
露米娅每次可以任取 连续的 未被吞噬 过的三部分,将其中的黑暗全部吞噬,并获得中间部分的饱食度。露米娅想知道,自己能获得的饱食度最大值是多少?
输入描述:第一行一个正整数n,代表道路被分的份数。
第二行有n个正整数ai,代表每一部分黑暗数量。
输出描述:一个正整数,代表最终饱食度的最大值。
思路:
dp
状态转移方程为:dp[i] = max(dp[i-1], dp[i-3] + a[i-1]);
cpp
#include<iostream>
using namespace std;
typedef long long LL;
const int N = 1e5 + 10;
int n;
int a[N];
LL dp[N];
int main()
{
cin >> n;
for(int i = 0; i < n; i++)
cin >> a[i];
dp[0]=dp[1]=0;
dp[2]=a[1];
for(int i = 3; i < n; i++)
dp[i] = max(dp[i-1], dp[i-3] + a[i-1]);
cout << dp[n - 1] << endl;
return 0;
}
Q3 难度⭐⭐⭐⭐
题目 :
dd被困在了一个诛幻森林,现在她面前有一条凶险的大河,河中央有,个神奇的浮块,浮块按1~n顺序标号,但两两并不相接,第i个浮块上有一个数字a[i],可能是正数,也可能是负数,每块浮块都附带一个魔法结界用于传送,当a[i]为正数时,dd可以选择传送到第i + k个浮块上,当dd抵达n号浮块时才可以顺利脱身,显然不管a[n]是多少,都没有任何意义,当a[i]为负时,dd只能选择标号小于等于i + a[i]的任意一块浮块进行传送,当i+ a[i] < 1时,默认只能传送到1的位置,每次传送都会花费1s的时间,随着时间的流逝,迷要森林的空气会被逐渐榨干,她现在在1号浮块,她想知道,她最快多久能顺利脱身,如果始终无法逃脱,请输出-1
输入描述:第一行一个数n(2≤n≤2000)
接下来一行n个数a[i](1≤|a[i]|≤2000)表示浮块上的数字
输出描述:输出一行,表示对应的答案
思路:
BFS找最短路
cpp
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
int main()
{
int n;
cin >> n;
vector<int> a(n + 1);
for (int i = 1; i <= n; ++i)
cin >> a[i];
vector<int> dist(n + 1, -1);
queue<int> q;
q.push(1);
dist[1] = 0;
while (!q.empty())
{
int cur = q.front();
q.pop();
for (int i = 1; i <= abs(a[cur]); ++i)
{
int next = a[cur] > 0 ? cur + i : cur - i;
if (next >= 1 && next <= n && dist[next] == -1)
{
dist[next] = dist[cur] + 1;
q.push(next);
}
}
}
cout << dist[n] << endl;
return 0;
}
day6
Q1 难度⭐
题目 :
有一个长为 n 的数组 A ,求满足 0 ≤ a ≤ b < n 的 A[b] - A[a] 的最大值。
给定数组 A 及它的大小 n ,请返回最大差值。
思路:
每次更新每一段的差的最大值
cpp
class Solution
{
public:
int getDis(vector<int>& A, int n)
{
int ans, min = A[0];
for(int i = 0; i < n; i ++)
{
if(A[i] < min)
min = A[i];
ans = max(ans, A[i] - min);
}
return ans;
}
};
Q2 难度⭐⭐
题目 :
给定数组arr,arr中所有的值都为正整数且不重复。每个值代表一种面值的货币,每种面值的货币可以使用任意张,再给定一个aim,代表要找的钱数,求组成aim的最少货币数。
如果无解,请返回-1.
输入描述:第一行给定两个正整数分别是 n 和 aim 分别表示数组 arr 的长度和要找的钱数。
第二行给定 n 个正整数表示 arr 数组中的所有元素
输出描述:输出组成 aim 的最少货币数
思路:
完全背包 动态规划问题-CSDN博客
cpp
#include <iostream>
#include <cstring>
using namespace std;
const int N = 1e4 + 10;
int v[N], w[N], f[N], n, V;
int main()
{
cin >> n >> V;
for (int i = 0; i < n; i++)
cin >> v[i];
memset(f, 0x3f, sizeof(f));
f[0] = 0;
for (int i = 0; i < n; i++)
for (int j = v[i]; j <= V; j++)
f[j] = min(f[j], f[j - v[i]] + 1);
if (f[V] > V)
f[V] = -1;
cout << f[V] << endl;
return 0;
}
Q3 难度⭐⭐⭐
题目 :
小红拿到了一个长度为nnn的字符串,她准备选取一段子串,满足该子串中字母的种类数量在[l,r]之间。小红想知道,一共有多少种选取方案?
输入描述:第一行输入三个正整数 n,l,r
第二行输入一个仅包含小写字母的字符串。
输出描述:合法的方案数。
思路:
双指针 + 前缀和
cpp
#include <iostream>
#include <string>
using namespace std;
int n, l, r;
string s;
long long find(int x, string s)
{
if(x == 0)
return 0;
int left = 0, right = 0;
int hash[26] = { 0 }, kinds = 0;
long long ans = 0;
while(right < n)
{
if(hash[s[right] - 'a']++ == 0) kinds++;
while(kinds > x)
{
if(hash[s[left] - 'a']-- == 1) kinds--;
left++;
}
ans += right - left + 1;
right++;
}
return ans;
}
int main()
{
cin >> n >> l >> r >> s;
cout << find(r, s) - find(l - 1, s) << endl;
return 0;
}