day1
Q1 难度⭐⭐⭐
题目:
笨小猴的词汇量很小,所以每次做英语选择题的时候都很头疼。但是他找到了一种方法,经试验证明,用这种方法去选择选项的时候选对的几率非常大!
这种方法的具体描述如下:假设maxn是单词中出现次数最多的字母的出现次数,minn是单词中出现次数最少的字母的出现次数,如果maxn-minn是一个质数,那么笨小猴就认为这是个Lucky Word,这样的单词很可能就是正确的答案。
输入描述:只有一行,是一个单词,其中只可能出现小写字母,并且长度小于100。
输出描述:共两行,第一行是一个字符串,假设输入的的单词是Lucky Word,那么输出“Lucky Word”,否则输出“No Answer”; 第二行是一个整数,如果输入单词是Lucky Word,输
出maxn-minn的值,否则输出0
思路:
prime算法判断质数
#include <iostream>
#include <cmath>
#include <string>
using namespace std;
bool prime(int n)
{
if (n < 2) return false;
if (n == 2) return true;
if (n % 2 == 0) return false;
for (int i = 3; i * i <= n; i += 2)
if (n % i == 0) return false;
return true;
}
int main()
{
int a[26] = {0};
string s;
int maxn = -0x3f3f3f, minn = 0x3f3f3f;
cin >> s;
for (int i = 0; i < s.length(); i++)
a[s[i] - 'a']++;
for (int i = 0; i < 26; i++)
{
if (a[i] > maxn) maxn = a[i];
if (a[i] < minn && a[i] != 0) minn = a[i];
}
if (prime(maxn - minn))
cout << "Lucky Word" << endl << maxn - minn << endl;
else
cout << "No Answer" << endl << 0 << endl;
return 0;
}
Q2 难度⭐
主持人调度(一)_牛客题霸_牛客网 (nowcoder.com)
题目:
有 n 个活动即将举办,每个活动都有开始时间与活动的结束时间,第 i 个活动的开始时间是 starti ,第 i 个活动的结束时间是 endi ,举办某个活动就需要为该活动准备一个活动主持人。
一位活动主持人在同一时间只能参与一个活动。并且活动主持人需要全程参与活动,换句话说,一个主持人参与了第 i 个活动,那么该主持人在 (starti,endi) 这个时间段不能参与其他任何活动。请问一个只有一个主持人能否举办全部活动。
数据范围: 1≤𝑛≤100000 , 0≤𝑠𝑡𝑎𝑟𝑡𝑖,𝑒𝑛𝑑𝑖≤1000000000
思路:
排序,后进行时间重叠判断
class Solution
{
public:
bool hostschedule(vector<vector<int> >& schedule)
{
sort(schedule.begin(), schedule.end());
int i = 0;
while(i < schedule.size() - 1)
{
if(schedule[i][1] > schedule[i+1][0])
return false;
i ++;
}
return true;
}
};
Q3 难度⭐⭐⭐⭐
分割等和子集_牛客题霸_牛客网 (nowcoder.com)
题目:
给定一个只包含正整数的数组 nums ,请问能否把这个数组取出若干个数使得取出的数之和和剩下的数之和相同。
数据范围: 1≤𝑛≤500 , 数组中的元素满足 1≤𝑛𝑢𝑚𝑠[𝑖]≤100
输入描述:第一行输入一个正整数 n ,表示数组 nums 的长度。
第二行输入 n 个正整数,表示数组中的值。
输出描述:如果满足题目条件,输出 true ,否则输出 false
思路:
01背包
状态转移方程为:dp[j] = max(dp[j], dp[j-nums[i]] + nums[i])
#include <iostream>
using namespace std;
const int N = 1000000;
bool dp(int nums[], int n)
{
int dp[N] = {0};
int sum = 0;
for(int i = 0; i < n; i ++)
{
sum += nums[i];
}
if(sum % 2 == 1)
return false;
int target = sum / 2;
for(int i = 0; i < n; i ++)
for(int j = target; j >= nums[i]; j --)
{
dp[j] = max(dp[j], dp[j-nums[i]] + nums[i]);
}
return dp[target] == target;
}
int main()
{
int n;
cin>>n;
int nums[n];
for(int i=0;i<n;i++)
cin>>nums[i];
if(dp(nums,n))
cout<<"true";
else
cout<<"false";
return 0;
}
day2
Q1 难度⭐⭐
题目:
小红拿到了一个只包含 'a' , 'b' , 'c' 三种字符的字符串。
小红想知道,这个字符串最短的、长度超过 1 的回文子串的长度是多少?
子串定义:字符串取一段连续的区间。例如"abcca"的子串有"ab"、"bcca"等,但"aca"则不是它的子串。
回文的定义:一个字符串正着读和倒着读都是相同的,那么定义它的回文的。
输入描述:一个只包含 'a' , 'b' , 'c' 三种字符的字符串。
数据范围:字符串长度不小于2,且不超过100
输出描述:如果不存在长度超过1的回文子串,则输出-1。
否则输出长度超过1的最短回文子串的长度。
思路:
※只有A,B,C 3中字母,所以答案只有-1,2,3 3种情况
#include <iostream>
using namespace std;
int main()
{
string s;
cin >> s;
for(int i = 0; i < s.size() - 1; i ++)
if(s[i] == s[i + 1])
{
cout << "2" << endl;
return 0;
}
for(int j = 0; j < s.size() - 2; j ++)
if(s[j] == s[j + 2])
{
cout << "3" << endl;
return 0;
}
cout << "-1" << endl;
return 0;
}
Q2 难度⭐⭐
题目:
小红拿到了一个数组。她想取一些不相邻的数,使得取出来的数之和尽可能大。你能帮帮她吗?
输入描述:第一行输入一个正整数 𝑛 ,代表数组长度
第二行输入 𝑛 个正整数 𝑎𝑖,代表整个数组。
1≤𝑛≤2∗10^5,1≤𝑎𝑖≤5∗10^3
输出描述:不相邻的数的最大和。
思路:
每次保留跳跃的数的和并判断目前的最大值
状态转移方程为:dp[i] = max(a[i] + dp[i - 2], dp[i - 1]);
#include <iostream>
#include <vector>
using namespace std;
int main()
{
int n;
cin >> n;
vector<long long> a(n);
vector<long long> dp(n, 0);
for(int i = 0; i < n; i ++)
{
cin >> a[i];
if(i == 0)
dp[0] = a[0];
else if(i == 1)
dp[1] = max(a[0], a[1]);
else
{
dp[i] = max(a[i] + dp[i - 2], dp[i - 1]);
}
}
cout << dp[n - 1] << endl;
return 0;
}
Q3 难度⭐⭐⭐⭐
题目:
dd作为集训队的队长,一直掌管着集训室的空调遥控器,她需要调整温度使队员们更好地进入训练状态,已知集训室一共有n名队员,每位队员都有一个温度诉求a[i](1≤i≤n),当室内温度为K时,当且仅当∣a[i]−K∣≤p时,这个队员能够正常进入训练状态,否则就会开始躁动,作为队长,dd需要调整好温度,她想知道,在最佳情况下,最多有多少队员同时进入训练状态
输入描述:第一行两个数n,p(1≤n,p≤1000000),含义如题面描述 |
接下来一行n个数a[i](1≤a[i]≤1000000)表示每个队员的温度诉求
输出描述:输出一个数字,表示最多有多少队员同时进入训练状态
思路:
区间头+,区间后-,遍历时总计
#include <iostream>
using namespace std;
int main()
{
int n, p;
cin >> n >> p;
int a[n], k[n], ans = 0;
for(int i = 0; i < n; i ++)
{
cin >> a[i];
k[max(a[i] - p, 0)] += 1;
k[a[i] + p + 1] -= 1;
}
for(int j = 1; j <= n; j ++)
{
k[j] += k[j - 1];
ans = max(ans, k[j]);
}
cout << ans << endl;
return 0;
}
day3
Q1 难度⭐⭐
B-kotori和气球_北京信息科技大学第十一届程序设计竞赛(重现赛) (nowcoder.com)
题目:
kotori最近迷上了摆气球的游戏。她一共有n种气球,每种气球有无数个。她要拿出若干个气球摆成一排。但是,由于气球被施放了魔法,同样种类的气球如果相邻会发生爆炸,因此若两个相邻的气球种类相同被视为不合法的。
kotori想知道,摆成一排m个一共有多少种不同的方案?由于该数可能过大,只需要输出其对109取模的结果。
输入描述:输入仅有一行,为两个整数n和m(1≤n,m≤100)
输出描述:输出一个整数,为方案数对109取模的结果。
思路:
总共n种,每次与某一种相邻的可能为n-1种
#include <iostream>
using namespace std;
int main()
{
int n, m, k;
cin >> n >> m;
k = n;
for(int i = 1; i < m; i ++)
{
k *= (n - 1);
k %= 109;
}
cout << k << endl;
return 0;
}
Q2 难度⭐⭐⭐
题目:
给定一个n x m的网格,在网格中每次在不超过边界的情况下可以选择向上、向下、向
左、向右移动一格。网格中的一些格子上放置有障碍物,放有障碍物的格子不能到达。
求从(x1,y1)到(x2,y2)最少的移动次数。若不能到达,输出-1。
输入描述:
第一行输入两个整数n,m(1 < n,m < 1000),表示网格大小。
第二行输入四个整数x1,y1,x2,y2(1≤x1,x2≤n, 1≤y1,y2 ≤ m),表示起点和终点的坐标。接下来的n行,每行输入一个长度为m的字符串。其中,第i行第1个字符表示第i行第j列的格子上的障碍物情况,若字符为"*",则格子上有障碍物,若字符为".",则格子上没有障碍物。保证起点不存在障碍物。
输出描述:
输出一行一个整数,表示从(x1,y1)到(x2,y2)最少的移动次数。
思路:
BFS
#include <iostream>
#include <queue>
#include <cstring>
using namespace std;
queue<pair<int, int> > q;
const int N = 1001;
char a[N][N];
int w[N][N];
bool b[N][N];
int xa, ya, xb, yb;
int n, m;
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
int bfs()
{
memset(w, -1, sizeof(w));
memset(b, 0, sizeof(b));
b[xa][ya] = 1;
q.push(make_pair(xa, ya));
while (!q.empty())
{
int x, y;
x = q.front().first, y = q.front().second;
q.pop();
for (int i = 0; i < 4; i++)
{
int nx = x + dx[i], ny = y + dy[i];
if (nx >= 1 && nx <= n && ny >= 1 && ny <= m && b[nx][ny] == false && a[nx][ny] == '.')
{
b[nx][ny] = true;
w[nx][ny] = w[x][y] + 1;
q.push(make_pair(nx, ny));
}
}
}
return w[xb][yb] >= 0 ? w[xb][yb] + 1 : -1;
}
int main()
{
cin >> n >> m;
cin >> xa >> ya >> xb >> yb;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
cin >> a[i][j];
int result = bfs();
if (result >= 0)
cout << result << endl;
else
cout << "-1" << endl;
return 0;
}
Q3 难度⭐⭐
主持人调度(二)_牛客题霸_牛客网 (nowcoder.com)
题目:
有 n 个活动即将举办,每个活动都有开始时间与活动的结束时间,第 i 个活动的开始时间是 starti ,第 i 个活动的结束时间是 endi ,举办某个活动就需要为该活动准备一个活动主持人。
一位活动主持人在同一时间只能参与一个活动。并且活动主持人需要全程参与活动,换句话说,一个主持人参与了第 i 个活动,那么该主持人在 (starti,endi) 这个时间段不能参与其他任何活动。求为了成功举办这 n 个活动,最少需要多少名主持人。
思路:
分开存下开始与结束的时间,同时遍历计算需要多少名主持人
class Solution
{
public:
int minmumNumberOfHost(int n, vector<vector<int> >& startEnd)
{
int ans = 0;
vector<int> begin;
vector<int> end;
for(int i = 0; i < startEnd.size(); i ++)
{
begin.push_back(startEnd[i][0]);
end.push_back(startEnd[i][1]);
}
sort(begin.begin(), begin.end());
sort(end.begin(), end.end());
int j = 0;
for(int i = 0; i < begin.size(); i ++)
{
if(begin[i] < end[j])
ans ++;
else
j ++;
}
return ans;
}
};
day4
Q1 难度⭐⭐
题目:
游游拿到了一个正整数,她希望你能重排这个正整数的数位,使得它变成偶数(不能有前导零)。你能帮帮她吗?注:重排后可以和原数相等。一共有 q 次询问。
输入描述:第一行输入一个正整数 q,代表询问次数。 接下来的 q行,每行输入一个正整数 x。
输出描述:输出q行,每行代表一次询问。
如果存在合法解,请输出一个重排后的正整数,务必保证其为偶数。
如果不存在合法解,直接输出-1。
思路:
判断偶数
#include <iostream>
#include <string>
using namespace std;
bool check(string s)
{
for(int i = 0; i < s.size(); i ++)
if ((s[i] % 2) == 0)
return true;
return false;
}
int main()
{
int q;
cin >> q;
while(q--)
{
long long x;
cin >> x;
string s = to_string(x);
if(!check(s))
cout << "-1" << endl;
else
{
if(s.back() % 2 == 0)
cout << s << endl;
else
{
for(int i = s.size() - 1; i >= 0; i --)
if(s[i] % 2 == 0)
{
swap(s[i], s[s.size() - 1]);
break;
}
cout << s << endl;
}
}
}
return 0;
}
Q2 难度⭐⭐⭐⭐
题目:
dd作为体操队队长,在给队员们排队形,体操队形为一个单独的纵列,体操队有n个同学,标号为1∼n,对于i(1≤i≤n)号队员,会有一个诉求(1≤a[i]≤n),表示他想排在a[i]号队员前面,当a[i]=i时,我们认为他没有位置需求,随便排哪儿都行,dd想知道有多少种队形方案,可以满足所有队员的要求。
输入描述:读入第一行一个数字n(2≤n≤10) 第二行n个数字,表示a[i],保证1≤a[i]≤n
输出描述:输出一行,表示方案数
思路:
两个数组,一个数组存序号,一个数组存需求,用序号找需求位置的序号,迭代判断是否存在可行解
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main()
{
int n;
cin >> n;
vector<int> a(n+1), b(n+1);
for (int i = 1; i <= n; ++i)
{
cin >> a[i];
b[i] = i;
}
int ans = 0;
do
{
bool flag = true;
for (int i = 1; i <= n; ++i)
{
if (b[i] <= b[a[i]]) continue;
flag = false;
break;
}
ans += flag;
} while (next_permutation(b.begin() + 1, b.end()));
cout << ans;
return 0;
}
Q3 难度⭐⭐⭐
二叉树中的最大路径和_牛客题霸_牛客网 (nowcoder.com)
题目:
二叉树里面的路径被定义为:从该树的任意节点出发,经过父=>子或者子=>父的连接,达到任意节点的序列。
注意:
1.同一个节点在一条二叉树路径里中最多出现一次
2.一条路径至少包含一个节点,且不一定经过根节点
给定一个二叉树的根节点root,请你计算它的最大路径和
思路:
DFS + 递归判断左右子树的最大路径和
class Solution
{
public:
int maxPathSum(TreeNode* root)
{
maxValue = INT_MIN;
dfs(root);
return maxValue;
}
int dfs(TreeNode* root)
{
if (root == NULL) return 0;
int l = max(0, dfs(root->left)); // 只有在左子树贡献大于0时才考虑
int r = max(0, dfs(root->right)); // 只有在右子树贡献大于0时才考虑
maxValue = max(maxValue, root->val + l + r); // 更新最大路径和
return root->val + max(l, r); // 返回当前子树的最大贡献值
}
private:
int maxValue = INT_MIN;
};
day5
Q1 难度⭐
排序子序列_牛客笔试题_牛客网 (nowcoder.com)
题目:
牛牛定义排序子序列为一个数组中一段连续的子序列,并且这段子序列是非递增或者非递减排序的。牛牛有一个长度为n的整数数组A,他现在有一个任务是把数组A分为若干段排序子序列,牛牛想知道他最少可以把这个数组分为几段排序子序列.
如样例所示,牛牛可以把数组A划分为[1,2,3]和[2,2,1]两个排序子序列,至少需要划分为2个排序子序列,所以输出2
输入描述:输入的第一行为一个正整数n(1 ≤ n ≤ 10^5)
第二行包括n个整数A_i(1 ≤ A_i ≤ 10^9),表示数组A的每个数字。
输出描述:输出一个整数表示牛牛可以将A最少划分为多少段排序子序列
思路:
遍历数组判断相邻数之间的关系
#include <iostream>
#include <vector>
using namespace std;
int main()
{
int n;
cin >> n;
vector<int> a(n);
for(int i = 0; i < n; i ++)
cin >> a[i];
int i = 0, ans = 0;
while(i < n)
{
if(a[i] < a[i + 1])
{
while(i < n && a[i] < a[i + 1])
i ++;
i ++;
ans += 1;
}
else if (a[i] == a[i + 1])
i ++;
else
{
while(i < n && a[i] >= a[i + 1])
i ++;
i ++;
ans += 1;
}
}
cout << ans << endl;
return 0;
}
Q2 难度⭐⭐⭐
题目:
给出一个正整数H,从1开始减,第一次必须减1,每次减的数字都必须和上一次相同或者是上一次的两倍,请问最少需要几次能把H恰好减到0。
输入描述:第一行给出一个正整数T
接下来T行每行一个H
输出描述:每行一个正整数代表最少的次数
思路:
设a为1,将数减a再判断能否被a的2倍整除,若能整除就将a翻倍
#include <iostream>
using namespace std;
int f(int x)
{
int ans = 0, a = 1;
while(x != 0)
{
x -= a;
ans ++;
if(x % (a * 2) == 0)
a *= 2;
}
return ans;
}
int main()
{
int t;
cin >> t;
while(t --)
{
int h;
cin >> h;
cout << f(h) << endl;
}
return 0;
}
Q3 难度⭐⭐⭐
最长上升子序列(二)_牛客题霸_牛客网 (nowcoder.com)
题目:
给定一个长度为 n 的数组a,求它的最长严格上升子序列的长度。
所谓子序列,指一个数组删掉一些数(也可以不删)之后,形成的新数组。例如 [1,5,3,7,3] 数组,其子序列有:[1,3,3]、[7] 等。但 [1,6]、[1,3,5] 则不是它的子序列。
要求:时间复杂度 𝑂(𝑛𝑙𝑜𝑔𝑛), 空间复杂度 𝑂(𝑛)
思路:
使用辅助数组存LIS,每次判断是否递增,若否则二分找第一个不小于a[i]的数并替换,保证不改变答案长度
class Solution
{
public:
int LIS(vector<int>& a)
{
if (a.empty()) return 0;
vector<int> dp;
dp.push_back(a[0]);
for (int i = 1; i < a.size(); ++i)
{
if (a[i] > dp.back())
{
dp.push_back(a[i]);
}
else
{
auto it = lower_bound(dp.begin(), dp.end(), a[i]);
*it = a[i];
}
}
return dp.size();
}
};
day6
Q1 难度⭐
题目:
牛妹是一个爱吃素的小女孩,所以很多素数都害怕被她吃掉。
一天,两个数字a和b为了防止被吃掉,决定和彼此相乘在一起,这样被吃掉的风险就会大大降低,但仍有一定的可能被吃掉,请你判断他们相乘后是否仍有被吃掉的风险。
也就是说,请你判断a×b是否是素数。
素数是指大于1的正整数中,有且仅有两个因子的数。
输入描述:输入第一行是一个整数T(1≤T≤10),表示测试组数。
接下来T行,每一行两个整数a,b。
输出描述:对于每一行输入,若输入满足a×b是素数,输出一行"YES",否则输出一行"NO"。
思路:
判断素数
#include <iostream>
#include <cmath>
using namespace std;
int T;
long long a, b;
bool isPrime(long long number)
{
if (number < 2)
return false;
if (number == 2)
return true;
if (number % 2 == 0)
return false;
for(int i = 3; i <= sqrt(number); i += 2)
if (number % i == 0)
return false;
return true;
}
int main()
{
cin >> T;
while(T --)
{
cin >> a >> b;
long long x = a * b;
cout << (isPrime(x) ? "YES" : "NO") << endl;
}
return 0;
}
Q2 难度⭐
相差不超过k的最多数_牛客题霸_牛客网 (nowcoder.com)
题目:
给定一个数组,选择一些数,要求选择的数中任意两数差的绝对值不超过 𝑘 。问最多能选择多少个数?
输入描述:第一行输入两个正整数 𝑛和𝑘。
第二行输入 𝑛 个正整数𝑎𝑖,用空格隔开,表示这个数组。
输出描述:一个正整数,代表能选的最多数量。
思路:
双指针
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int main()
{
int n, k;
cin >> n >> k;
vector<int> a(n);
for(int i = 0; i < n; i ++)
cin >> a[i];
sort(a.begin(), a.end());
int l = 0, r = 1, ans = 1;
for(; r < n; r ++)
{
while(a[r] - a[l] > k)
l ++;
ans = max(ans, r - l + 1);
}
cout << ans << endl;
return 0;
}
Q3 难度⭐⭐⭐
最长公共子序列(一)_牛客题霸_牛客网 (nowcoder.com)
题目:
给定两个字符串 s1 和 s2,长度为 n 和 m 。求两个字符串最长公共子序列的长度。
所谓子序列,指一个字符串删掉部分字符(也可以不删)形成的字符串。例如:字符串 "arcaea" 的子序列有 "ara" 、 "rcaa" 等。但 "car" 、 "aaae" 则不是它的子序列。
所谓 s1 和 s2 的最长公共子序列,即一个最长的字符串,它既是 s1 的子序列,也是 s2 的子序列。
输入描述:第一行输入一个整数 n 和 m ,表示字符串 s1 和 s2 的长度。
接下来第二行和第三行分别输入一个字符串 s1 和 s2。
输出描述:输出两个字符串的最长公共子序列的长度
思路:
动态规划
状态转移方程为:
- 如果 s1[i - 1] == s2[j - 1],那么 dp[i][j] = dp[i - 1][j - 1] + 1。
- 否则,dp[i][j] = max(dp[i - 1][j], dp[i][j - 1])。
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main()
{
int n, m;
cin >> n >> m;
string s1, s2;
cin >> s1 >> s2;
vector<vector<int>> dp(n + 1, vector<int>(m + 1, 0));
for (int i = 1; i <= n; i ++)
for (int j = 1; j <= m; j ++)
{
if (s1[i - 1] == s2[j - 1])
dp[i][j] = dp[i - 1][j - 1] + 1;
else
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
}
cout << dp[n][m] << endl;
return 0;
}