移动非代码随想录体系题目到卡码网仓库

This commit is contained in:
programmercarl
2024-09-14 12:01:45 +08:00
parent 25003d7826
commit 2de1a8647f
47 changed files with 0 additions and 3984 deletions

View File

@ -1,27 +0,0 @@
# 111. 构造二阶行列式
暴力模拟就好,每个数不超过 20 暴力枚举其实也没多大。
```CPP
#include <iostream>
using namespace std;
int main() {
int n;
cin >> n;
for (int x = 1; x <= 20; x++) {
for (int y = 1; y <= 20; y++) {
for (int i = 1; i <= 20; i++) {
for (int j = 1; j <= 20; j++) {
if ((x * j - y * i) == n) {
cout << x << " " << y << endl;
cout << i << " " << j << endl;
return 0;
}
}
}
}
}
cout << -1 << endl;
}
```

View File

@ -1,26 +0,0 @@
# 112. 挑战boss
本题题意有点绕,注意看一下 题目描述中的【提示信息】,但是在笔试中,是不给这样的提示信息的。
简单模拟:
```CPP
#include<iostream>
using namespace std;
int main() {
int n, a, b, k = 0;
cin >> n >> a >> b;
string s;
cin >> s;
int result = 0;
for (int i = 0; i < s.size(); i++) {
int cur = a + k * b;
result += cur;
++k;
if (s[i] == 'x') k = 0;
}
cout << result << endl;
return 0;
}
```

View File

@ -1,59 +0,0 @@
# 113.国际象棋
广搜,但本题如果广搜枚举马和象的话会超时。
广搜要只枚举马的走位,同时判断是否在对角巷直接走象
```CPP
#include <iostream>
using namespace std;
const int N = 100005, mod = 1000000007;
using ll = long long;
int n, ans;
int dir[][2] = {{1, 2}, {1, -2}, {-1, 2}, {-1, -2}, {2, 1}, {2, -1}, {-2, -1}, {-2, 1}};
int main() {
int x1, y1, x2, y2;
cin >> n;
while (n--) {
scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
if (x1 == x2 && y1 == y2) {
cout << 0 << endl;
continue;
}
// 判断象走一步到达
int d = abs(x1 - x2) - abs(y1 - y2);
if (!d) {cout << 1 << endl; continue;}
// 判断马走一步到达
bool one = 0;
for (int i = 0; i < 8; ++i) {
int dx = x1 + dir[i][0], dy = y1 + dir[i][1];
if (dx == x2 && dy == y2) {
cout << 1 << endl;
one = true;
break;
}
}
if (one) continue;
// 接下来为两步的逻辑, 象走两步或者马走一步,象走一步
// 象直接两步可以到达,这个计算是不是同颜色的格子,象可以在两步到达所有同颜色的格子
int d2 = abs(x1 - x2) + abs(y1 - y2);
if (d2 % 2 == 0) {
cout << 2 << endl;
continue;
}
// 接下来判断马 + 象的组合
bool two = 0;
for (int i = 0; i < 8; ++i) {
int dx = x1 + dir[i][0], dy = y1 + dir[i][1];
int d = abs(dx - x2) - abs(dy - y2);
if (!d) {cout << 2 << endl; two = true; break;}
}
if (two) continue;
// 剩下的格子全都是三步到达的
cout << 3 << endl;
}
return 0;
}
```

View File

@ -1,29 +0,0 @@
# 114. 小欧的平均数
这道题非常的脑筋急转弯, 读题都要理解半天。
初步读题,感觉好像是求 如何最小加减,得到三个数的平均数。
但题意不是这样的。
小欧的说的三个数平衡,只是三个数里 任何两个数 相加都能被2整除 那么 也就是说,这三个数 要么都是 奇数,要么都是偶数,才能达到小欧所说的平衡。
所以题目要求的就是三个数最小加减1 几次 可以让三个数都变成奇数,或者都变成偶数。
所以最终的结果 不是1 就是0没有其他的。
录友可能想,题目出的这么绕干啥? 没办法,企业的笔试题就是这样的。
```CPP
#include<iostream>
#include<algorithm>
using namespace std;
int main() {
int x, y, z;
cin >> x >> y >> z;
int count = (x % 2 == 0) + (y % 2 == 0) + (z % 2 == 0);
cout << min(3 - count, count);
}
```

View File

@ -1,67 +0,0 @@
# 115. 组装手机
这道题是比较难得哈希表题目。 把代码随想录哈希表章节理解透彻,做本题没问题。
思路是
1. 用哈希表记录 外壳售价 和 手机零件售价 出现的次数
2. 记录总和出现的次数
3. 遍历总和,减去 外壳售价,看 手机零件售价出现了几次
4. 最后累加,取最大值
有一个需要注意的点: 数字可以重复,在计算个数的时候,如果计算重复的数字
例如 如果输入是
```
4
1 1 1 1
1 1 1 1
```
那么输出应该是 4 外壳售价 和 手机零件售价 是可以重复的。
代码如下:
```CPP
#include <iostream>
#include <vector>
#include <unordered_set>
#include <unordered_map>
using namespace std;
int main() {
int n;
cin >> n;
vector<int> aVec(n, 0);
vector<int> bVec(n, 0);
unordered_map<int, int > aUmap;
unordered_map<int, int > bUmap;
for (int i = 0; i < n; i++) {
cin >> aVec[i];
aUmap[aVec[i]]++;
}
for (int i = 0; i < n; i++) {
cin >> bVec[i];
bUmap[bVec[i]]++;
}
unordered_set<int > uset;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++){
uset.insert(aVec[i] + bVec[j]);
}
}
int result = 0;
for (int sum : uset) {
//cout << p.first << endl;
int count = 0;
for (pair<int, int> p : aUmap) {
//cout << p.first - aVec[i] << endl;
if (sum - p.first > 0 && bUmap[sum - p.first] != 0) {
count += min(bUmap[sum - p.first], p.second);
}
}
result = max(result, count);
}
cout << result << endl;
}
```

View File

@ -1,91 +0,0 @@
<p align="center"><strong><a href="./qita/join.md">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们受益!</strong></p>
# 大数减法
本题测试数据超过int 和 longlong了所以考察的使用 string 来模拟 两个大数的 加减操作。
当然如果使用python或者Java 使用库函数都可以水过。
使用字符串来模拟过程,需要处理以下几个问题:
* 负号处理:要考虑正负数的处理,如果大数相减的结果是负数,需要在结果前加上负号。
* 大数比较:在进行减法之前,需要确定哪个数大,以便知道结果是否需要添加负号。
* 位数借位:处理大数相减时的借位问题,这类似于手动减法。
```CPP
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
// 比较两个字符串表示的数字返回1表示a > b0表示a == b-1表示a < b
int compareStrings(const string& a, const string& b) {
if (a.length() > b.length()) return 1;
if (a.length() < b.length()) return -1;
return a.compare(b);
}
// 去除字符串左侧的前导零
string removeLeadingZeros(const string& num) {
size_t start = 0;
while (start < num.size() && num[start] == '0') {
start++;
}
return start == num.size() ? "0" : num.substr(start);
}
// 大数相减假设a >= b
string subtractStrings(const string& a, const string& b) {
string result;
int len1 = a.length(), len2 = b.length();
int carry = 0;
for (int i = 0; i < len1; i++) {
int digitA = a[len1 - 1 - i] - '0';
int digitB = i < len2 ? b[len2 - 1 - i] - '0' : 0;
int digit = digitA - digitB - carry;
if (digit < 0) {
digit += 10;
carry = 1;
} else {
carry = 0;
}
result.push_back(digit + '0');
}
// 去除结果中的前导零
reverse(result.begin(), result.end());
return removeLeadingZeros(result);
}
string subtractLargeNumbers(const string& num1, const string& num2) {
string a = num1, b = num2;
// 比较两个数的大小
int cmp = compareStrings(a, b);
if (cmp == 0) {
return "0"; // 如果两个数相等结果为0
} else if (cmp < 0) {
// 如果a < b交换它们并在结果前加上负号
swap(a, b);
return "-" + subtractStrings(a, b);
} else {
return subtractStrings(a, b);
}
}
int main() {
string num1, num2;
cin >> num1 >> num2;
string result = subtractLargeNumbers(num1, num2);
cout << result << endl;
return 0;
}
```

View File

@ -1,132 +0,0 @@
# 121. 小红的区间翻转
比较暴力的方式,就是直接模拟, 枚举所有 区间,然后检查其翻转的情况。
在检查翻转的时候,需要一些代码优化,否则容易超时。
```CPP
#include <iostream>
#include <vector>
using namespace std;
bool canTransform(const vector<int>& a, const vector<int>& b, int left, int right) {
// 提前检查翻转区间的值是否可以匹配
for (int i = left, j = right; i <= right; i++, j--) {
if (a[i] != b[j]) {
return false;
}
}
// 检查翻转区间外的值是否匹配
for (int i = 0; i < left; i++) {
if (a[i] != b[i]) {
return false;
}
}
for (int i = right + 1; i < a.size(); i++) {
if (a[i] != b[i]) {
return false;
}
}
return true;
}
int main() {
int n;
cin >> n;
vector<int> a(n);
vector<int> b(n);
for (int i = 0; i < n; i++) {
cin >> a[i];
}
for (int i = 0; i < n; i++) {
cin >> b[i];
}
int count = 0;
// 遍历所有可能的区间
for (int left = 0; left < n; left++) {
for (int right = left; right < n; right++) {
// 检查翻转区间 [left, right] 后a 是否可以变成 b
if (canTransform(a, b, left, right)) {
count++;
}
}
}
cout << count << endl;
return 0;
}
```
也可以事先计算好,最长公共前缀,和最长公共后缀。
在公共前缀和公共后缀之间的部分进行翻转操作,这样我们可以减少很多不必要的翻转尝试。
通过在公共前缀和后缀之间的部分,找到可以通过翻转使得 a 和 b 相等的区间。
以下 为评论区 卡码网用户码鬼的C++代码
```CPP
#include <iostream>
#include <vector>
using namespace std;
int main() {
int n;
cin >> n;
vector<int> a(n), b(n);
for (int i = 0; i < n; i++) {
cin >> a[i];
}
for (int i = 0; i < n; i++) {
cin >> b[i];
}
vector<int> prefix(n, 0), suffix(n, 0);
// 计算前缀相等的位置
int p = 0;
while (p < n && a[p] == b[p]) {
prefix[p] = 1;
p++;
}
// 计算后缀相等的位置
int s = n - 1;
while (s >= 0 && a[s] == b[s]) {
suffix[s] = 1;
s--;
}
int count = 0;
// 遍历所有可能的区间
for (int i = 0; i < n - 1; i++) {
for (int j = i + 1; j < n; j++) {
// 判断前缀和后缀是否相等
if ((i == 0 || prefix[i - 1] == 1) && (j == n - 1 || suffix[j + 1] == 1)) {
// 判断翻转后的子数组是否和目标数组相同
bool is_palindrome = true;
for (int k = 0; k <= (j - i) / 2; k++) {
if (a[i + k] != b[j - k]) {
is_palindrome = false;
break;
}
}
if (is_palindrome) {
count++;
}
}
}
}
cout << count << endl;
return 0;
}
```

View File

@ -1,127 +0,0 @@
# 滑动窗口最大值
本题是 [代码随想录:滑动窗口最大值](https://www.programmercarl.com/0239.%E6%BB%91%E5%8A%A8%E7%AA%97%E5%8F%A3%E6%9C%80%E5%A4%A7%E5%80%BC.html) 的升级版。
在[代码随想录:滑动窗口最大值](https://www.programmercarl.com/0239.%E6%BB%91%E5%8A%A8%E7%AA%97%E5%8F%A3%E6%9C%80%E5%A4%A7%E5%80%BC.html) 中详细讲解了如何求解 滑动窗口的最大值。
那么求滑动窗口的最小值原理也是一样的, 大家稍加思考,把优先级队列里的 大于 改成小于 就行了。
求最大值的优先级队列(从大到小)
```
while (!que.empty() && value > que.back()) {
```
求最小值的优先级队列(从小到大)
```
while (!que.empty() && value > que.back()) {
```
这样在滑动窗口里 最大值最小值都求出来了,遍历一遍找出 差值最大的就好。
至于输入,需要一波字符串处理,比较考察基本功。
CPP代码如下
```CPP
#include <iostream>
#include <sstream>
#include <vector>
#include <string>
#include <deque>
using namespace std;
class MyBigQueue { //单调队列(从大到小)
public:
deque<int> que; // 使用deque来实现单调队列
// 每次弹出的时候,比较当前要弹出的数值是否等于队列出口元素的数值,如果相等则弹出。
// 同时pop之前判断队列当前是否为空。
void pop(int value) {
if (!que.empty() && value == que.front()) {
que.pop_front();
}
}
// 如果push的数值大于入口元素的数值那么就将队列后端的数值弹出直到push的数值小于等于队列入口元素的数值为止。
// 这样就保持了队列里的数值是单调从大到小的了。
void push(int value) {
while (!que.empty() && value > que.back()) {
que.pop_back();
}
que.push_back(value);
}
// 查询当前队列里的最大值 直接返回队列前端也就是front就可以了。
int front() {
return que.front();
}
};
class MySmallQueue { //单调队列(从小到大)
public:
deque<int> que;
void pop(int value) {
if (!que.empty() && value == que.front()) {
que.pop_front();
}
}
// 和上面队列的区别是这里换成了小于,
void push(int value) {
while (!que.empty() && value < que.back()) {
que.pop_back();
}
que.push_back(value);
}
int front() {
return que.front();
}
};
int main() {
string input;
getline(cin, input);
vector<int> nums;
int k;
// 找到并截取nums的部分
int numsStart = input.find('[');
int numsEnd = input.find(']');
string numsStr = input.substr(numsStart + 1, numsEnd - numsStart - 1);
// cout << numsStr << endl;
// 用字符串流处理nums字符串提取数字
stringstream ss(numsStr);
string temp;
while (getline(ss, temp, ',')) {
nums.push_back(stoi(temp));
}
// 找到并提取k的值
int kStart = input.find("k = ") + 4;
k = stoi(input.substr(kStart));
MyBigQueue queB; // 获取区间最大值
MySmallQueue queS; // 获取区间最小值
// vector<int> result;
for (int i = 0; i < k; i++) { // 先将前k的元素放进队列
queB.push(nums[i]);
queS.push(nums[i]);
}
int result = queB.front() - queS.front();
for (int i = k; i < nums.size(); i++) {
queB.pop(nums[i - k]); // 滑动窗口移除最前面元素
queB.push(nums[i]); // 滑动窗口前加入最后面的元素
queS.pop(nums[i - k]);
queS.push(nums[i]);
result = max (result, queB.front() - queS.front());
}
cout << result << endl;
}
```

View File

@ -1,52 +0,0 @@
121. 小红的数组构造
本题大家不要想着真去模拟数组的情况,那样就想复杂了。
数组只能是1k、2k、3k ... (n-1)k、nk这样 总和就是最小的。
注意最后的和可能超过int所以用 long long。
代码如下:
```CPP
#include <iostream>
using namespace std;
int main () {
long long result = 0;
int n, k;
cin >> n >> k;
for (int i = 1; i <= n; i++) {
result += i * k;
}
cout << result << endl;
}
```
优化思路:
由于要计算1到n的整数之和可以利用等差数列求和公式来优化计算。
和公式1 + 2 + 3 + ... + n = n * (n + 1) / 2
因此,总和 result = k * (n * (n + 1) / 2)
```CPP
#include <iostream>
using namespace std;
int main() {
long long result = 0;
int n, k;
cin >> n >> k;
// 使用等差数列求和公式进行计算
result = k * (n * (n + 1LL) / 2);
cout << result << endl;
return 0;
}
```

View File

@ -1,38 +0,0 @@
# 122.精华帖子
开辟一个数组默认都是0把精华帖标记为1.
使用前缀和快速计算出k 范围内 有多少个精华帖。
前缀和要特别注意区间问题,即 vec[i+k] - vec[i] 求得区间和是 (i, i + k] 这个区间,注意这是一个左开右闭的区间。
所以前缀和 很容易漏掉 vec[0] 这个数值的计算
```CPP
#include <iostream>
#include <vector>
using namespace std;
int main() {
int n, m, k, l, r;
cin >> n >> m >> k;
vector<int> vec(n);
while (m--) {
cin >> l >> r;
for (int i = l; i < r; i++) vec[i] = 1;
}
int result = 0;
for (int i = 0; i < k; i++) result += vec[i]; // 提前预处理result包含vec[0]的区间,否则前缀和容易漏掉这个区间
for (int i = 1; i < n; i++) {
vec[i] += vec[i - 1];
}
for (int i = 0; i < n - k; i++) {
result = max (result, vec[i + k] - vec[i]);
}
cout << result << endl;
}
```

View File

@ -1,66 +0,0 @@
# 123.连续子数组最大和
这道题目可以说是 [代码随想录,动态规划:最大子序和](https://www.programmercarl.com/0053.%E6%9C%80%E5%A4%A7%E5%AD%90%E5%BA%8F%E5%92%8C%EF%BC%88%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%EF%BC%89.html) 的升级版。
题目求的是 可以替换一个数字 之后 的 连续子数组最大和。
如果替换的是数组下标 i 的元素。
那么可以用 [代码随想录,动态规划:最大子序和](https://www.programmercarl.com/0053.%E6%9C%80%E5%A4%A7%E5%AD%90%E5%BA%8F%E5%92%8C%EF%BC%88%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%EF%BC%89.html) 的方法,先求出 [0 - i) 区间的 最大子序和 dp1 和 (i, n)的最大子序和dp2 。
然后在遍历一遍i 计算 dp1 + dp2 + vec[i] 的最大值就可以。
正序遍历,求出 [0 - i) 区间的 最大子序dp[ i - 1] 表示 是 包括下标i - 1以vec[i - 1]为结尾的最大连续子序列和为dp[i - 1]。
所以 在计算区间 (i, n)即 dp2 的时候,我们要倒叙。 因为我们求的是以 包括下标i + 1 为起始位置的最大连续子序列和为dp[i + 1]。
这样 dp1 + dp2 + vec[i] 才是一个完整区间。
这里就体现出对 dp数组定义的把控本题如果对 dp数组含义理解不清其实是不容易做出来的。
代码:
```CPP
#include <iostream>
#include <vector>
#include <climits>
using namespace std;
int main() {
int t, n, x;
cin >> t;
while (t--) {
cin >> n >> x;
vector<int> vec(n);
for (int i = 0; i < n; i++) cin >> vec[i];
vector<int> dp1(n);
dp1[0] = vec[0];
int res = vec[0];
// 从前向后统计最大子序和
for (int i = 1; i < n; i++) {
dp1[i] = max(dp1[i - 1] + vec[i], vec[i]); // 状态转移公式
res = max(res, dp1[i]);
}
res = max(res, vec[n - 1]);
// 从后向前统计最大子序和
vector<int> dp2(n);
dp2[n - 1] = vec[n - 1];
for (int i = n - 2; i >= 0; i--) {
dp2[i] = max(dp2[i + 1] + vec[i], vec[i]);
}
for (int i = 0 ; i < n ; i++) {
int dp1res = 0;
if (i > 0) dp1res = max(dp1[i-1], 0);
int dp2res = 0;
if (i < n - 1 ) dp2res = max(dp2[i+1], 0);
res = max(res, dp1res + dp2res + x);
}
cout << res << endl;
}
}
```

View File

@ -1,29 +0,0 @@
# 小美的排列询问
模拟题,注意 x 和y 不分先后
```CPP
#include <iostream>
#include <vector>
using namespace std;
int main() {
int n, x, y;
cin >> n;
vector<int> vec(n, 0);
for (int i =0; i < n; i++) {
cin >> vec[i];
}
cin >> x >> y;
for (int i = 0; i < n - 1; i++) {
if (x == vec[i] && y == vec[i + 1]) || (y == vec[i] && x == vec[i + 1]) ) {
cout << "Yes" << endl;
return 0;
}
}
cout << "No" << endl;
}
```

View File

@ -1,35 +0,0 @@
# 小美走公路
在处理环形情况的时候,很多录友容易算懵了,不是多算一个数,就是少算一个数。
这里这样的题目,最好的方式是将 两个环展开,首尾相连,这样我们就可以通过 直线的思维去解题了
两个注意点:
1. x 可以比 y 大,题目没规定 x 和y 的大小顺序
2. 累计相加的数可能超过int
```CPP
#include <iostream>
#include <vector>
using namespace std;
int main () {
int n;
cin >> n;
vector<int> vec(2* n + 1, 0);
for (int i = 1; i <= n; i++) {
cin >> vec[i];
vec[n + i] = vec[i];
}
int x, y;
cin >> x >> y;
int xx = min(x ,y); // 注意点1x 可以比 y 大
int yy = max(x, y);
long long a = 0, b = 0; // 注意点2相加的数可能超过int
for (int i = xx; i < yy; i++) a += vec[i];
for (int i = yy; i < xx + n; i++ ) b += vec[i];
cout << min(a, b) << endl;
}
```

View File

@ -1,51 +0,0 @@
# 小美的蛋糕切割
二维前缀和,不了解前缀和的录友 可以自行查一下,是一个很容易理解的算法思路
```CPP
#include <iostream>
#include <vector>
#include <climits>
using namespace std;
int main () {
int n, m;
cin >> n >> m;
int sum = 0;
vector<vector<int>> vec(n, vector<int>(m, 0)) ;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
cin >> vec[i][j];
sum += vec[i][j];
}
}
// 统计横向
vector<int> horizontal(n, 0);
for (int i = 0; i < n; i++) {
for (int j = 0 ; j < m; j++) {
horizontal[i] += vec[i][j];
}
}
// 统计纵向
vector<int> vertical(m , 0);
for (int j = 0; j < m; j++) {
for (int i = 0 ; i < n; i++) {
vertical[j] += vec[i][j];
}
}
int result = INT_MAX;
int horizontalCut = 0;
for (int i = 0 ; i < n; i++) {
horizontalCut += horizontal[i];
result = min(result, abs(sum - horizontalCut - horizontalCut));
}
int verticalCut = 0;
for (int j = 0; j < m; j++) {
verticalCut += vertical[j];
result = min(result, abs(sum - verticalCut - verticalCut));
}
cout << result << endl;
}
```

View File

@ -1,78 +0,0 @@
# 130.小美的字符串变换
本题是[岛屿数量](./0099.岛屿的数量广搜.md)的进阶版,主要思路和代码都是一样的,统计一个图里岛屿的数量,也是染色问题。
1、 先枚举各个可能出现的矩阵
2、 针对矩阵经行广搜染色(深搜,并查集一样可以)
3、 统计岛屿数量最小的数量。
```CPP
#include <iostream>
#include <climits>
#include <vector>
#include <queue>
using namespace std;
// 广搜代码同 卡码网99. 岛屿数量
int dir[4][2] = {0, 1, 1, 0, -1, 0, 0, -1}; // 四个方向
void bfs(const vector<vector<char>>& grid, vector<vector<bool>>& visited, int x, int y, char a) {
queue<pair<int, int>> que;
que.push({x, y});
visited[x][y] = true; // 只要加入队列,立刻标记
while(!que.empty()) {
pair<int ,int> cur = que.front(); que.pop();
int curx = cur.first;
int cury = cur.second;
for (int i = 0; i < 4; i++) {
int nextx = curx + dir[i][0];
int nexty = cury + dir[i][1];
if (nextx < 0 || nextx >= grid.size() || nexty < 0 || nexty >= grid[0].size()) continue; // 越界了,直接跳过
if (!visited[nextx][nexty] && grid[nextx][nexty] == a) {
que.push({nextx, nexty});
visited[nextx][nexty] = true; // 只要加入队列立刻标记
}
}
}
}
int main() {
int n;
string s;
cin >> n;
int result = INT_MAX;
cin >> s;
for (int k = 1; k < n; k++) {
if (n % k != 0) continue;
// 计算出 矩阵的 行 和 列
int x = n / k;
int y = k;
//cout << x << " " << y << endl;
vector<vector<char>> vec(x, vector<char>(y, 0));
// 填装矩阵
int sCount = 0;
for (int i = 0; i < x; i++) {
for (int j = 0; j < y; j++) {
vec[i][j] = s[sCount++];
}
}
// 开始广搜染色
vector<vector<bool>> visited(x, vector<bool>(y, false));
int count = 0;
for (int i = 0; i < x; i++) {
for (int j = 0; j < y; j++) {
if (!visited[i][j]) {
count++; // 遇到没访问过的陆地,+1
bfs(vec, visited, i, j, vec[i][j]); // 将与其链接的陆地都标记上 true
}
}
}
// 取岛屿数量最少的
result = min (result, count);
}
cout << result << endl;
}
```

View File

@ -1,134 +0,0 @@
# 131. 小美的树上染色
本题为树形dp 稍有难度,主要在于 递推公式上。
dp数组的定义
dp[cur][1] :当前节点染色,那么当前节点为根节点及其左右子节点中,可以染色的最大数量
dp[cur][0] :当前节点不染色,那么当前节点为根节点及其左右子节点中,可以染色的最大数量
关于 dp转移方程
1、 情况一:
如果当前节点不染色,那就去 子节点 染色 或者 不染色的最大值。
`dp[cur][0] += max(dp[child][0], dp[child][1]);`
2、情况二
那么当前节点染色的话,这种情况就不好想了。
首先这不是二叉树,每一个节点都有可能 会有n个子节点。
所以我们要分别讨论,每一个子节点的情况 对父节点的影响。
那么父节点 针对每种情况,就要去 最大值, 也就是 `dp[cur][1] = max(dp[cur][1], 每个自孩子的情况)`
如图假如节点1 是我们要计算的父节点节点2是我们这次要计算的子节点。
![](https://code-thinking-1253855093.file.myqcloud.com/pics/20240617204601.png)
选中一个节点2 作为我们这次计算的子节点,父节点染色的话,子节点必染色。
接下来就是计算 父节点1和该子节点2染色的话 以子节点2 为根的 染色节点的最大数量 。
节点2不染色 且 以节点2为根节点的最大 染色数量 + 2 + 2 是因为 节点 1 和 节点2 要颜色了,染色节点增加两个。
代码:`dp[child][0] + 2`
细心的录友会发现,那我们只计算了 红色框里面的,那么框外 最大的染色数量是多少呢?
![](https://code-thinking-1253855093.file.myqcloud.com/pics/20240617205709.png)
先看 作为子节点的节点2 为根节点的最大染色数量是多少? 取一个最值,即 节点2染色 或者 不染色取最大值。
代码:`max(dp[child][0], dp[child][1])`
那么红框以外的 染色最大节点数量 就是 `dp[cur][0] - max(dp[child][0], dp[child][1])`
cur是节点1child是节点2
红框以外的染色最大数量 + 父节点1和该子节点2染色的话 以子节点2 为根的 染色节点的最大数量 就是 节点1 染色的最大节点数量。
代码:
`dp[cur][1] = max(dp[cur][1], dp[cur][0] - max(dp[child][0], dp[child][1]) + dp[child][0] + 2);`
整体代码如下:
```CPP
#include <iostream>
#include <vector>
#include <cmath>
#include <algorithm>
#include <list>
using namespace std;
int maxN = 10005;
vector<vector<int>> dp (maxN, vector<int>(2, 0));
vector<list<int>> grid(maxN); // 邻接表
vector<long> value(maxN); // 存储每个节点的权值
// 在树上进行动态规划的函数
void dpOnTheTree(int cur) {
for (int child : grid[cur]) {
// 后序遍历,从下向上计算
dpOnTheTree(child);
// 情况一
dp[cur][0] += max(dp[child][0], dp[child][1]);
}
// 计算dp[1] - 当前节点染色
for (int child : grid[cur]) {
long mul = value[cur] * value[child]; // 当前节点和相邻节点权值的乘积
long sqrtNum = (long) sqrt(mul);
if (sqrtNum * sqrtNum == mul) { // 如果乘积是完全平方数
// 情况二
// dp[cur][0] 表示所有子节点 染色或者不染色的 最大染色数量
// max(dp[child][0], dp[child][1]) 需要染色节点的孩子节点的最大染色数量
// dp[cur][0] - max(dp[child][0], dp[child][1]) 除了要染色的节点及其子节点,其他孩子的最大染色数量
// 最后 + dp[child][0] + 2 就是本节点染色的最大染色节点数量
dp[cur][1] = max(dp[cur][1], dp[cur][0] - max(dp[child][0], dp[child][1]) + dp[child][0] + 2);
}
}
}
int main() {
int n;
cin >> n; // 输入节点数量
// 读取节点权值
for (int i = 1; i <= n; ++i) {
cin >> value[i];
}
// 构建树的邻接表
for (int i = 1; i < n; ++i) {
int x, y;
cin >> x >> y;
grid[x].push_back(y);
}
// 从根节点节点1开始进行动态规划
dpOnTheTree(1);
// 输出最大染色节点数量
cout << max(dp[1][0], dp[1][1]) << endl;
return 0;
}
```

View File

@ -1,47 +0,0 @@
# 132. 夹吃棋
[题目链接](https://kamacoder.com/problempage.php?pid=1209)
这道题是模拟题,但很多录友可能想复杂了。
行方向,白棋吃,只有这样的布局 `o*o`,黑棋吃,只有这样的布局 `*o*`
列方向也是同理的。
想到这一点,本题的代码就容易写了, C++代码如下:
```CPP
#include <iostream>
#include <vector>
using namespace std;
int main() {
int n;
cin >> n;
while (n--) {
int black = 0, white = 0;
vector<string> grid(3, "");
// 判断行
for (int i = 0; i < 3; i++) {
cin >> grid[i];
if (grid[i] == "o*o") white++;
if (grid[i] == "*o*") black++;
}
// 判断列
for (int i = 0; i < 3; i++) {
string s;
s += grid[0][i];
s += grid[1][i];
s += grid[2][i];
if (s == "o*o") white++;
if (s == "*o*") black++;
}
// 如果一个棋盘的局面没有一方被夹吃或者黑白双方都被对面夹吃,则认为是平局
if ((!white && !black) || (white && black)) cout << "draw" << endl;
// 白棋赢
else if (white && !black) cout << "yukan" << endl;
// 黑棋赢
else cout << "kou" << endl;
}
}
```

View File

@ -1,77 +0,0 @@
# 134. 皇后移动的最小步数
[题目链接](https://kamacoder.com/problempage.php?pid=1211)
本题和 [代码随想录-不同路径](https://www.programmercarl.com/0062.%E4%B8%8D%E5%90%8C%E8%B7%AF%E5%BE%84.html) 有一些类似。
关键是弄清楚递推公式
一共分三个情况,
情况一,向右移动:
然后从 (i, j) 再向右走 到 (i, k)。 无论k 多大步数只加1
`dp[i][k] = dp[i][j] + 1`
那么 `dp[i][k]` 也有可能 从其他方向得到,例如 从上到下, 或者斜上方到达 dp[i][k]
本题我们要求最小步数,所以取最小值:`dp[i][k] = min(dp[i][k], dp[i][j] + 1);`
情况二,向下移动:
从 (i, j) 再向下走 到 (k, j)。 无论k 多大步数只加1
`dp[k][j] = dp[i][j] + 1;`
同理 `dp[i][k]` 也有可能 从其他方向得到,取最小值:`dp[k][j] = min(dp[k][j], dp[i][j] + 1);`
情况三,右下方移动:
从 (i, j) 再向右下方移动 到 (i + k, j + k)。 无论k 多大步数只加1
`dp[i + k][j + k] = dp[i][j] + 1`
同理 `dp[i + k][j + k]` 也有可能 从其他方向得到,取最小值:`dp[i + k][j + k] = min(dp[i + k][j + k], dp[i][j] + 1);`
```CPP
#include <iostream>
#include <vector>
using namespace std;
const int INF = 4e6; // 最多步数也就是 2000 * 2000
int main() {
int n, m;
cin >> n >> m;
vector<vector<char>> grid(n, vector<char>(m));
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
cin >> grid[i][j];
}
}
vector<vector<int>> dp(n, vector<int>(m, INF));
dp[0][0] = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (grid[i][j] == '*') continue;
// 向右移动k个格子
for (int k = j + 1; k < m && grid[i][k] == '.'; k++) {
dp[i][k] = min(dp[i][k], dp[i][j] + 1);
}
// 向下移动 k个格子
for (int k = i + 1; k < n && grid[k][j] == '.'; k++) {
dp[k][j] = min(dp[k][j], dp[i][j] + 1);
}
// 向右下移动k个格子
for (int k = 1; i + k < n && j + k < m && grid[i + k][j + k] == '.'; k++) {
dp[i + k][j + k] = min(dp[i + k][j + k], dp[i][j] + 1);
}
}
}
if (dp[n - 1][m - 1] == INF) cout << -1 << endl;
else cout << dp[n - 1][m - 1] << endl;
}
```

View File

@ -1,147 +0,0 @@
# 135. 获取连通的相邻节点列表
本题是一个 “阅读理解”题,其实题目的算法很简单,但理解题意很费劲。
题目描述中的【提示信息】 是我后加上去了,华为笔试的时候没有这个 【提示信息】。
相信没有 【提示信息】大家理解题意 平均要多用半个小时。
思路:
1. 将第一行数据加入set中
2. 后面输出数据,判断是否在 set里
3. 最后把结果排个序
```CPP
#include <iostream>
#include <vector>
#include <algorithm>
#include <unordered_set>
using namespace std;
int main() {
unordered_set<int> uset;
int n, a;
cin >> n;
while (n--) {
cin >> a;
uset.insert(a);
}
int m, x, vlan_id;
long long tb;
vector<long long> vecTB;
cin >> m;
while(m--) {
cin >> tb;
cin >> x;
vector<long long> vecVlan_id(x);
for (int i = 0; i < x; i++) {
cin >> vecVlan_id[i];
}
for (int i = 0; i < x; i++) {
if (uset.find(vecVlan_id[i]) != uset.end()) {
vecTB.push_back(tb);
break;
}
}
}
cout << vecTB.size() << endl;
if (vecTB.size() != 0) {
sort(vecTB.begin(), vecTB.end());
for (int i = 0; i < vecTB.size() ; i++) cout << vecTB[i] << " ";
}
}
```
## 其他语言版本
### Java
```Java
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
Set<Integer> uset = new HashSet<>();
int n = scanner.nextInt();
while (n-- > 0) {
int a = scanner.nextInt();
uset.add(a);
}
int m = scanner.nextInt();
List<Long> vecTB = new ArrayList<>();
while (m-- > 0) {
long tb = scanner.nextLong();
int x = scanner.nextInt();
List<Integer> vecVlan_id = new ArrayList<>();
for (int i = 0; i < x; i++) {
vecVlan_id.add(scanner.nextInt());
}
for (int vlanId : vecVlan_id) {
if (uset.contains(vlanId)) {
vecTB.add(tb);
break;
}
}
}
System.out.println(vecTB.size());
if (!vecTB.isEmpty()) {
Collections.sort(vecTB);
for (long tb : vecTB) {
System.out.print(tb + " ");
}
}
}
}
```
### Python
```python
def main():
import sys
input = sys.stdin.read
data = input().split()
index = 0
n = int(data[index])
index += 1
uset = set()
for _ in range(n):
a = int(data[index])
index += 1
uset.add(a)
m = int(data[index])
index += 1
vecTB = []
while m > 0:
tb = int(data[index])
index += 1
x = int(data[index])
index += 1
vecVlan_id = []
for _ in range(x):
vecVlan_id.append(int(data[index]))
index += 1
for vlan_id in vecVlan_id:
if vlan_id in uset:
vecTB.append(tb)
break
m -= 1
print(len(vecTB))
if vecTB:
vecTB.sort()
print(" ".join(map(str, vecTB)))
if __name__ == "__main__":
main()
```

View File

@ -1,148 +0,0 @@
# 字符串处理器
纯模拟,但情况比较多,非常容易 空指针异常。
大家要注意,边界问题 以及 负数问题。
整体代码如下:
```CPP
#include<bits/stdc++.h>
using namespace std;
int main() {
int index = 0;
long long optNum;
string s;
string cmd;
while(cin >> cmd){
//cout << s << endl;
if(cmd == "insert") {
string buff;
cin >> buff;
s.insert(index, buff);
index += buff.size();
}
else if(cmd == "move") {
cin >> optNum;
if(optNum > 0 && index + optNum <= s.size()) index += optNum;
if(optNum < 0 && index >= -optNum) index += optNum;
}
else if(cmd == "delete") {
cin >> optNum;
if(index >= optNum && optNum > 0){
s.erase(index - optNum, optNum);
index -= optNum;
}
}
else if(cmd == "copy") {
if(index > 0) {
string tmp = s.substr(0, index);
s.insert(index, tmp);
}
}
else if(cmd == "end") {
for(int i = 0; i < index; i++) {
cout << s[i];
}
cout << '|';
for(int i = index; i < s.size(); i++) cout << s[i];
break;
}
}
return 0;
}
```
## 其他语言版本
### Java
```Java
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
StringBuilder s = new StringBuilder();
int index = 0;
int optNum;
while (true) {
String cmd = scanner.next();
if (cmd.equals("insert")) {
String buff = scanner.next();
s.insert(index, buff);
index += buff.length();
} else if (cmd.equals("move")) {
optNum = scanner.nextInt();
if (optNum > 0 && index + optNum <= s.length()) index += optNum;
if (optNum < 0 && index >= -optNum) index += optNum;
} else if (cmd.equals("delete")) {
optNum = scanner.nextInt();
if (index >= optNum && optNum > 0) {
s.delete(index - optNum, index);
index -= optNum;
}
} else if (cmd.equals("copy")) {
if (index > 0) {
String tmp = s.substring(0, index);
s.insert(index, tmp);
}
} else if (cmd.equals("end")) {
System.out.print(s.substring(0, index) + '|' + s.substring(index));
break;
}
}
scanner.close();
}
}
```
### Python
```python
def main():
import sys
input = sys.stdin.read
data = input().split()
s = ""
index = 0
i = 0
while i < len(data):
cmd = data[i]
i += 1
if cmd == "insert":
buff = data[i]
i += 1
s = s[:index] + buff + s[index:]
index += len(buff)
elif cmd == "move":
optNum = int(data[i])
i += 1
if optNum > 0 and index + optNum <= len(s):
index += optNum
elif optNum < 0 and index >= -optNum:
index += optNum
elif cmd == "delete":
optNum = int(data[i])
i += 1
if index >= optNum and optNum > 0:
s = s[:index - optNum] + s[index:]
index -= optNum
elif cmd == "copy":
if index > 0:
tmp = s[:index]
s = s[:index] + tmp + s[index:]
elif cmd == "end":
print(s[:index] + '|' + s[index:])
break
if __name__ == "__main__":
main()
```

View File

@ -1,192 +0,0 @@
# 137. 消息传输
这道题目,普通广搜就可以解决。
这里说一下几点注意事项:
1、 题目描述中,注意 n 是列数m是行数
这是造成很多录友周赛的时候提交 返回 【运行错误】的罪魁祸首,如果 输入用例是 正方形,那没问题,如果后台输入用例是矩形, n 和 m 搞反了,就会数组越界。
矩阵是 m * n ,但输入的顺序却是 先输入n 再输入 m。
这会让很多人把矩阵的 n 和 m 搞反。
其实规范出题就应该是n 行m列然后 先输入n在输入m。
只能说 大厂出题的人,也不是专业出题的,所以会在 非算法方面一不小心留下很多 “bug”消耗大家的精力。
2、再写广搜的时候可能担心会无限循环
即 A 走到 BB又走到AA又走到B ,这种情况,一般来说 广搜都是用一个 visit数组来标记的。
但本题不用,因为 不会重复走的,题图里的信号都是正数,根据距离判断大小 可以保证不走回头路。
```CPP
#include <iostream>
#include <queue>
#include <vector>
using namespace std;
const int inf = 1e6;
int main () {
int n, m, startx, starty;
cin >> n >> m;
cin >> startx >> starty;
vector<vector<int>> grid(m, vector<int>(n));
vector<vector<int>> dis(m, vector<int>(n, inf));
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
cin >> grid[i][j];
}
}
queue<pair<int, int>> que;
int dir[4][2] = {0, 1, 1, 0, -1, 0, 0, -1};
que.push(pair<int, int>(startx, starty));
dis[startx][starty] = 0;
while(!que.empty()) {
pair<int, int> cur = que.front(); que.pop();
for (int i = 0; i < 4; i++) {
int newx = cur.first + dir[i][1];
int newy = cur.second + dir[i][0];
if (newx < 0 || newx >= m || newy < 0 || newy >= n || grid[cur.first][cur.second] == 0) continue;
if (dis[newx][newy] > dis[cur.first][cur.second] + grid[cur.first][cur.second]) {
dis[newx][newy] = dis[cur.first][cur.second] + grid[cur.first][cur.second];
que.push(pair<int, int>(newx, newy));
}
}
}
int result = 0;
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (dis[i][j] == inf) {
cout << -1 << endl;
return 0;
}
result = max(result, dis[i][j]);
}
}
cout << result << endl;
}
```
## 其他语言版本
### Java
```Java
import java.util.*;
public class Main {
static final int INF = 1000000;
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
int m = scanner.nextInt();
int startX = scanner.nextInt();
int startY = scanner.nextInt();
int[][] grid = new int[m][n];
int[][] dis = new int[m][n];
for (int i = 0; i < m; i++) {
Arrays.fill(dis[i], INF);
for (int j = 0; j < n; j++) {
grid[i][j] = scanner.nextInt();
}
}
Queue<int[]> queue = new LinkedList<>();
int[][] directions = {{0, 1}, {1, 0}, {-1, 0}, {0, -1}};
queue.add(new int[]{startX, startY});
dis[startX][startY] = 0;
while (!queue.isEmpty()) {
int[] current = queue.poll();
for (int[] dir : directions) {
int newX = current[0] + dir[0];
int newY = current[1] + dir[1];
if (newX >= 0 && newX < m && newY >= 0 && newY < n && grid[current[0]][current[1]] != 0) {
if (dis[newX][newY] > dis[current[0]][current[1]] + grid[current[0]][current[1]]) {
dis[newX][newY] = dis[current[0]][current[1]] + grid[current[0]][current[1]];
queue.add(new int[]{newX, newY});
}
}
}
}
int result = 0;
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (dis[i][j] == INF) {
System.out.println(-1);
return;
}
result = Math.max(result, dis[i][j]);
}
}
System.out.println(result);
scanner.close();
}
}
```
### Python
```Python
from collections import deque
inf = 1000000
def main():
import sys
input = sys.stdin.read
data = input().split()
index = 0
n = int(data[index])
m = int(data[index+1])
startx = int(data[index+2])
starty = int(data[index+3])
index += 4
grid = []
dis = [[inf] * n for _ in range(m)]
for i in range(m):
grid.append([int(data[index+j]) for j in range(n)])
index += n
directions = [(0, 1), (1, 0), (-1, 0), (0, -1)]
queue = deque()
queue.append((startx, starty))
dis[startx][starty] = 0
while queue:
curx, cury = queue.popleft()
for dx, dy in directions:
newx, newy = curx + dx, cury + dy
if 0 <= newx < m and 0 <= newy < n and grid[curx][cury] != 0:
if dis[newx][newy] > dis[curx][cury] + grid[curx][cury]:
dis[newx][newy] = dis[curx][cury] + grid[curx][cury]
queue.append((newx, newy))
result = 0
for i in range(m):
for j in range(n):
if dis[i][j] == inf:
print(-1)
return
result = max(result, dis[i][j])
print(result)
if __name__ == "__main__":
main()
```

View File

@ -1,101 +0,0 @@
# 可爱串
整体思路,就含有 子序列的字符串数量 减去 含有子串的字符串数量。
因为子序列数量已经是包含子串数量的。 剩下的就是 只有子序列 且没有子串的 字符串数量。
需要注意我们求的不是 长度为 i 的字符串里有多少个 red 子序列。
**而是 可以有多少个 长度为i 的字符串 含有子序列 red**
同理可以有多少个长度为i的字符串含有 red 子串
认清这一点很重要!
### 求子串
dp2[i][3] 长度为i 且 含有子串 red 的字符串数量 有多少
dp2[i][2] 长度为i 且 含有子串 re 的字符串数量有多少
dp2[i][1] 长度为 i 且 含有子串 r 的字符串数量有多少
dp2[1][0] 长度为 i 且 含有 只有 de, ee , e, d的字符串的字符串数量有多少。
```CPP
// 求子串
dp2[0][0] = 1;
for(int i = 1;i <= n; i++) {
dp2[i][0] = (dp2[i - 1][2] + dp2[i - 1][1] + dp2[i - 1][0] * 2) % mod; // 含有 re 的可以把 r改成d 含有r 的可以改成
dp2[i][1] = (dp2[i - 1][2] + dp2[i - 1][1] + dp2[i - 1][0]) % mod;
dp2[i][2] = (dp2[i - 1][1]);
dp2[i][3] = (dp2[i - 1][3] * 3 + dp2[i - 1][2]) % mod;
}
``
### 求子序列
dp1[i][3] 长度为i 且 含有子序列 red 的字符串数量 有多少
dp2[i][2] 长度为i 且 含有子序列 re 的字符串数量有多少
dp2[i][1] 长度为 i 且 含有子序列 r 的字符串数量有多少
dp2[1][0] 长度为 i 且 含有 只含有 e 和 d 的字符串的字符串数量有多少。
```CPP
// 求子序列
dp1[0][0]=1;
for(int i=1;i<=n;i++)
{
dp1[i][0] = (dp1[i - 1][0] * 2) % mod;
dp1[i][1] = (dp1[i - 1][0] + dp1[i - 1][1] * 2) % mod;
dp1[i][2] = (dp1[i - 1][1] + dp1[i - 1][2] * 2) % mod;
dp1[i][3] = (dp1[i - 1][2] + dp1[i - 1][3] * 3) % mod;
}
```
```CPP
#include <bits/stdc++.h>
using namespace std;
using ll=long long;
const int mod=1e9+7;
int main()
{
int n;
cin>>n;
vector<vector<ll>> dp1(n + 1,vector<ll> (4,0));
vector<vector<ll>> dp2(n + 1,vector<ll> (4,0));
// 求子串
dp2[0][0] = 1;
for(int i = 1;i <= n; i++) {
dp2[i][0] = (dp2[i - 1][2] + dp2[i - 1][1] + dp2[i - 1][0] * 2) % mod;
dp2[i][1] = (dp2[i - 1][2] + dp2[i - 1][1] + dp2[i - 1][0]) % mod;
dp2[i][2] = (dp2[i - 1][1]);
dp2[i][3] = (dp2[i - 1][3] * 3 + dp2[i - 1][2]) % mod;
}
// 求子序列
dp1[0][0]=1;
for(int i=1;i<=n;i++)
{
dp1[i][0] = (dp1[i - 1][0] * 2) % mod;
dp1[i][1] = (dp1[i - 1][0] + dp1[i - 1][1] * 2) % mod;
dp1[i][2] = (dp1[i - 1][1] + dp1[i - 1][2] * 2) % mod;
dp1[i][3] = (dp1[i - 1][2] + dp1[i - 1][3] * 3) % mod;
}
cout<<(dp1[n][3] - dp2[n][3])%mod;
}
```

View File

@ -1,29 +0,0 @@
```CPP
#include <iostream>
#include <vector>
using namespace std;
int countOnes(long long num) {
int zeroCount = 0;
while (num > 0) {
if (num % 10 != 0) { // 检查最低位是否为0
zeroCount++;
}
num /= 10; // 移除最低位
}
return zeroCount;
}
int main() {
int n;
cin >> n;
vector<int> vec(n);
for (int i = 0; i < n; i++) cin >> vec[i];
int result = 0;
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
if (countOnes(vec[i] * vec[j]) == 1) result++;
}
}
cout << result << endl;
}
```

View File

@ -1,104 +0,0 @@
本题和 [96.不同的二叉搜索树](https://www.programmercarl.com/0096.%E4%B8%8D%E5%90%8C%E7%9A%84%E4%BA%8C%E5%8F%89%E6%90%9C%E7%B4%A2%E6%A0%91.html) 比较像
* 取模这里很容易出错
* 过程中所用到的数值都有可能超过int所以要改用longlong
```CPP
#include <iostream>
#include <vector>
using namespace std;
long long mod = 1e9 + 7;
long long dp(int t, vector<long long>& memory) {
if (t % 2 == 0) return 0;
if (t == 1) return 1;
if (memory[t] != -1) return memory[t];
long long result = 0;
// 枚举左右子树节点的数量
for (int i = 1; i < t; i += 2) {
long long leftNum = dp(i, memory); // 左子树节点数量为i
long long rightNum = dp(t - i - 1, memory); // 右子树节点数量为t - i - 1
result += (leftNum * rightNum) % mod; // 注意这里是乘的关系
result %= mod;
}
memory[t] = result;
return result;
}
int main() {
int n;
cin >> n;
vector<long long> memory(n + 1, -1);
cout << dp(n, memory) << endl;
}
```
```CPP
#include <iostream>
#include <vector>
#include <cstring>
using namespace std;
const int MOD = 1000000007;
int main() {
int num;
cin >> num;
if (num % 2 == 0) {
cout << 0 << endl;
return 0;
}
vector<long long> dp(num + 1, 0);
dp[1] = 1;
for (int i = 3; i <= num; i += 2) {
for (int j = 1; j <= i - 2; j += 2) {
dp[i] = (dp[i] + dp[j] * dp[i - 1 - j]) % MOD;
}
}
cout << dp[num] << endl;
return 0;
}
```
第二题的代码
#include <bits/stdc++.h>
using namespace std;
long fastexp(long base,long n,long mod){
long answer = 1;
while(n > 0){
if(n % 2 == 1){
answer = (answer * base) % mod;
}
base = (base * base) % mod;
n /= 2;
}
return answer;
}
int kawaiiStrings(int n) {
// write code here
std::vector<long> f(n + 1), g(n + 1), h(n + 1);
long mod = 1000000007;
for (long i = 2; i <= n; i++) g[i] = (g[i - 1] * 2 + (i - 1) * fastexp(2,i-2,mod)) % mod;
for (long i = 3; i <= n; i++) f[i] = ((f[i - 1] * 3) % mod + g[i - 1]) % mod;
for (long i = 3; i <= n; i++) h[i] = (fastexp(3, i - 3, mod) + h[i - 1] * 3 - h[i - 3]) % mod;
return (f[n]-h[n]+mod)%mod;
}
int main(){
int n;
cin >> n;
cout << kawaiiStrings(n) << endl;
return 0;
}

View File

@ -1,108 +0,0 @@
# 142. 两个字符串的最小 ASCII 删除总和
本题和[代码随想录:两个字符串的删除操作](https://www.programmercarl.com/0583.%E4%B8%A4%E4%B8%AA%E5%AD%97%E7%AC%A6%E4%B8%B2%E7%9A%84%E5%88%A0%E9%99%A4%E6%93%8D%E4%BD%9C.html) 思路基本是一样的。
属于编辑距离问题,如果想彻底了解,建议看看「代码随想录」的编辑距离总结篇。
本题dp数组含义
dp[i][j] 表示 以i-1为结尾的字符串word1和以j-1位结尾的字符串word2想要达到相等所需要删除元素的最小ASCII 删除总和。
如果 s1[i - 1] 与 s2[j - 1] 相同,则不用删:`dp[i][j] = dp[i - 1][j - 1]`
如果 s1[i - 1] 与 s2[j - 1] 不相同删word1 的 最小删除和: `dp[i - 1][j] + s1[i - 1]` 删word2的最小删除和 `dp[i][j - 1] + s2[j - 1]`
取最小值: `dp[i][j] = min(dp[i - 1][j] + s1[i - 1], dp[i][j - 1] + s2[j - 1])`
```CPP
#include <iostream>
#include <vector>
using namespace std;
int main() {
string s1, s2;
cin >> s1 >> s2;
vector<vector<int>> dp(s1.size() + 1, vector<int>(s2.size() + 1, 0));
// s1 如果变成空串的最小删除ASCLL值综合
for (int i = 1; i <= s1.size(); i++) dp[i][0] = dp[i - 1][0] + s1[i - 1];
// s2 如果变成空串的最小删除ASCLL值综合
for (int j = 1; j <= s2.size(); j++) dp[0][j] = dp[0][j - 1] + s2[j - 1];
for (int i = 1; i <= s1.size(); i++) {
for (int j = 1; j <= s2.size(); j++) {
if (s1[i - 1] == s2[j - 1]) dp[i][j] = dp[i - 1][j - 1];
else dp[i][j] = min(dp[i - 1][j] + s1[i - 1], dp[i][j - 1] + s2[j - 1]);
}
}
cout << dp[s1.size()][s2.size()] << endl;
}
```
### Java
```Java
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String s1 = scanner.nextLine();
String s2 = scanner.nextLine();
int[][] dp = new int[s1.length() + 1][s2.length() + 1];
// s1 如果变成空串的最小删除ASCII值综合
for (int i = 1; i <= s1.length(); i++) {
dp[i][0] = dp[i - 1][0] + s1.charAt(i - 1);
}
// s2 如果变成空串的最小删除ASCII值综合
for (int j = 1; j <= s2.length(); j++) {
dp[0][j] = dp[0][j - 1] + s2.charAt(j - 1);
}
for (int i = 1; i <= s1.length(); i++) {
for (int j = 1; j <= s2.length(); j++) {
if (s1.charAt(i - 1) == s2.charAt(j - 1)) {
dp[i][j] = dp[i - 1][j - 1];
} else {
dp[i][j] = Math.min(dp[i - 1][j] + s1.charAt(i - 1), dp[i][j - 1] + s2.charAt(j - 1));
}
}
}
System.out.println(dp[s1.length()][s2.length()]);
scanner.close();
}
}
```
### python
```python
def min_delete_sum(s1: str, s2: str) -> int:
dp = [[0] * (len(s2) + 1) for _ in range(len(s1) + 1)]
# s1 如果变成空串的最小删除ASCII值综合
for i in range(1, len(s1) + 1):
dp[i][0] = dp[i - 1][0] + ord(s1[i - 1])
# s2 如果变成空串的最小删除ASCII值综合
for j in range(1, len(s2) + 1):
dp[0][j] = dp[0][j - 1] + ord(s2[j - 1])
for i in range(1, len(s1) + 1):
for j in range(1, len(s2) + 1):
if s1[i - 1] == s2[j - 1]:
dp[i][j] = dp[i - 1][j - 1]
else:
dp[i][j] = min(dp[i - 1][j] + ord(s1[i - 1]), dp[i][j - 1] + ord(s2[j - 1]))
return dp[len(s1)][len(s2)]
if __name__ == "__main__":
s1 = input().strip()
s2 = input().strip()
print(min_delete_sum(s1, s2))
```

View File

@ -1,237 +0,0 @@
# 143. 最长同值路径
本题两个考点:
1. 层序遍历构造二叉树
2. 树形dp找出最长路径
对于写代码不多,或者动手能力比较差的录友,第一个 构造二叉树 基本就被卡主了。
```CPP
#include <iostream>
#include <queue>
#include <vector>
using namespace std;
// 定义二叉树节点结构
struct TreeNode {
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
// 根据层序遍历数组构建二叉树
TreeNode* constructBinaryTree(const vector<string>& levelOrder) {
if (levelOrder.empty()) return NULL;
TreeNode* root = new TreeNode(stoi(levelOrder[0]));
queue<TreeNode*> q;
q.push(root);
int i = 1;
while (!q.empty() && i < levelOrder.size()) {
TreeNode* current = q.front();
q.pop();
if (i < levelOrder.size() && levelOrder[i] != "null") {
current->left = new TreeNode(stoi(levelOrder[i]));
q.push(current->left);
}
i++;
if (i < levelOrder.size() && levelOrder[i] != "null") {
current->right = new TreeNode(stoi(levelOrder[i]));
q.push(current->right);
}
i++;
}
return root;
}
int result = 0;
// 树形DP
int dfs(TreeNode* node) {
if (node == NULL) return 0;
int leftPath = dfs(node->left);
int rightPath = dfs(node->right);
int leftNum = 0, rightNum = 0;
if (node->left != NULL && node->left->val == node->val) {
leftNum = leftPath + 1;
}
if (node->right != NULL && node->right->val == node->val) {
rightNum = rightPath + 1;
}
result = max(result, leftNum + rightNum);
return max(leftNum, rightNum);
}
int main() {
int n;
cin >> n;
vector<string> levelOrder(n);
for (int i = 0; i < n ; i++) cin >> levelOrder[i];
TreeNode* root = constructBinaryTree(levelOrder);
dfs(root);
cout << result << endl;
return 0;
}
```
### Java
```Java
import java.util.*;
class TreeNode {
int val;
TreeNode left, right;
TreeNode(int x) {
val = x;
left = null;
right = null;
}
}
public class Main {
public static int result = 0;
public static TreeNode constructBinaryTree(List<String> levelOrder) {
if (levelOrder.isEmpty()) return null;
TreeNode root = new TreeNode(Integer.parseInt(levelOrder.get(0)));
Queue<TreeNode> queue = new LinkedList<>();
queue.add(root);
int i = 1;
while (!queue.isEmpty() && i < levelOrder.size()) {
TreeNode current = queue.poll();
if (i < levelOrder.size() && !levelOrder.get(i).equals("null")) {
current.left = new TreeNode(Integer.parseInt(levelOrder.get(i)));
queue.add(current.left);
}
i++;
if (i < levelOrder.size() && !levelOrder.get(i).equals("null")) {
current.right = new TreeNode(Integer.parseInt(levelOrder.get(i)));
queue.add(current.right);
}
i++;
}
return root;
}
public static int dfs(TreeNode node) {
if (node == null) return 0;
int leftPath = dfs(node.left);
int rightPath = dfs(node.right);
int leftNum = 0, rightNum = 0;
if (node.left != null && node.left.val == node.val) {
leftNum = leftPath + 1;
}
if (node.right != null && node.right.val == node.val) {
rightNum = rightPath + 1;
}
result = Math.max(result, leftNum + rightNum);
return Math.max(leftNum, rightNum);
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
sc.nextLine(); // consume the newline character
List<String> levelOrder = new ArrayList<>();
for (int i = 0; i < n; i++) {
levelOrder.add(sc.next());
}
TreeNode root = constructBinaryTree(levelOrder);
dfs(root);
System.out.println(result);
sc.close();
}
}
```
### python
```python
from typing import List, Optional
from collections import deque
import sys
class TreeNode:
def __init__(self, val: int = 0, left: 'TreeNode' = None, right: 'TreeNode' = None):
self.val = val
self.left = left
self.right = right
def construct_binary_tree(level_order: List[str]) -> Optional[TreeNode]:
if not level_order:
return None
root = TreeNode(int(level_order[0]))
queue = deque([root])
i = 1
while queue and i < len(level_order):
current = queue.popleft()
if i < len(level_order) and level_order[i] != "null":
current.left = TreeNode(int(level_order[i]))
queue.append(current.left)
i += 1
if i < len(level_order) and level_order[i] != "null":
current.right = TreeNode(int(level_order[i]))
queue.append(current.right)
i += 1
return root
result = 0
def dfs(node: Optional[TreeNode]) -> int:
global result
if node is None:
return 0
left_path = dfs(node.left)
right_path = dfs(node.right)
left_num = right_num = 0
if node.left is not None and node.left.val == node.val:
left_num = left_path + 1
if node.right is not None and node.right.val == node.val:
right_num = right_path + 1
result = max(result, left_num + right_num)
return max(left_num, right_num)
if __name__ == "__main__":
input = sys.stdin.read
data = input().strip().split()
n = int(data[0])
level_order = data[1:]
root = construct_binary_tree(level_order)
dfs(root)
print(result)
```

View File

@ -1,66 +0,0 @@
# 0144.字典序最小的01字符串
贪心思路:移动尽可能 移动前面的1 ,这样可以是 字典序最小
从前到后遍历,遇到 0 ,就用前面的 1 来交换
```CPP
#include <iostream>
#include <string>
using namespace std;
int main() {
int n,k;
cin >> n >> k;
string s;
cin >> s;
for(int i = 0; i < n && k > 0; i++) {
if(s[i] == '0') {
// 开始用前面的 1 来交换
int j = i;
while(j > 0 && s[j - 1] == '1' && k > 0) {
swap(s[j], s[j - 1]);
--j;
--k;
}
}
}
cout << s << endl;
return 0;
}
```
Java
```Java
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
int k = scanner.nextInt();
scanner.nextLine(); // 消耗掉换行符
String s = scanner.nextLine();
char[] ch = s.toCharArray();
for (int i = 0; i < n && k > 0; i++) {
if (ch[i] == '0') {
// 开始用前面的 1 来交换
int j = i;
while (j > 0 && ch[j - 1] == '1' && k > 0) {
char tmp = ch[j];
ch[j] = ch[j - 1];
ch[j - 1] = tmp;
j--;
k--;
}
}
}
System.out.println(new String(ch));
}
}
```

View File

@ -1,98 +0,0 @@
# 145. 数组子序列的排列
每个元素出现的次数相乘就可以了。
注意 “长度为 m 的数组1 到 m 每个元素都出现过,且恰好出现 1 次。” 题目中有n个元素所以我们要统计的就是 1 到 n 元素出现的个数。
因为如果有一个元素x 大于n了 那不可能出现 长度为x的数组 且 1 到 x 每个元素都出现过。
```CPP
#include "bits/stdc++.h"
using namespace std;
int main(){
int n;
int x;
cin >> n;
unordered_map<int, int> umap;
for(int i = 0; i < n; ++i){
cin >> x;
if(umap.find(x) != umap.end()) umap[x]++;
else umap[x] = 1;
}
long long res = 0;
long long num = 1;
for (int i = 1; i <= n; i++) {
if (umap.find(i) == umap.end()) break; // 如果i都没出现后面得数也不能 1 到 m 每个元素都出现过
num = (num * umap[i]) % 1000000007;
res += num;
res %= 1000000007;
}
cout << res << endl;
}
```
```Java
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < n; i++) {
int x = sc.nextInt();
map.put(x, map.getOrDefault(x, 0) + 1);
}
long res = 0;
long num = 1;
for (int i = 1; i <= n; i++) {
if (!map.containsKey(i)) break; // 如果i都没出现后面得数也不能1到m每个元素都出现过
num = (num * map.get(i)) % 1000000007;
res += num;
res %= 1000000007;
}
System.out.println(res);
sc.close();
}
}
```
```python
def main():
import sys
input = sys.stdin.read
data = input().split()
n = int(data[0])
umap = {}
for i in range(1, n + 1):
x = int(data[i])
if x in umap:
umap[x] += 1
else:
umap[x] = 1
res = 0
num = 1
MOD = 1000000007
for i in range(1, n + 1):
if i not in umap:
break # 如果i都没出现后面得数也不能1到m每个元素都出现过
num = (num * umap[i]) % MOD
res = (res + num) % MOD
print(res)
if __name__ == "__main__":
main()
```

View File

@ -1,65 +0,0 @@
# 146. 传送树
本题题意是比较绕的,我后面给补上了 【提示信息】对 题目输出样例讲解一下,相对会容易理解的多。
```CPP
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
vector<vector<int>> edge; // 邻接表来存图
vector<int> nxt;
int n;
/*
* 递归函数用于找到每个节点的下一个传送门节点并记录在nxt数组中。
* 遍历当前节点的所有子节点递归调用findNext以确保子节点的nxt值已经计算出来。
* 更新当前节点的nxt值为其子节点中编号最小的节点。
* 如果当前节点是叶子节点即没有子节点则将其nxt值设置为自身。
*/
void findNext(int node) {
for (int v : edge[node]) {
findNext(v);
if (nxt[node] == -1 || nxt[node] > min(v, nxt[v])) {
nxt[node] = min(v, nxt[v]);
}
}
// 叶子节点
if (nxt[node] == -1) {
nxt[node] = node;
}
}
// 计算从节点u出发经过若干次传送门到达叶子节点所需的步数。
// 通过不断访问nxt节点直到到达叶子节点记录访问的节点数。
int get(int u) {
int cnt = 1;
while (nxt[u] != u) {
cnt++;
u = nxt[u];
}
return cnt;
}
int main() {
cin >> n;
edge.resize(n + 1);
nxt.resize(n + 1, -1);
for (int i = 1; i <= n; ++i) {
int a, b;
cin >> a >> b;
edge[a].push_back(b);
}
findNext(1);
for (int i = 1; i <= n; ++i) {
cout << get(i) << ' ';
}
}
```

View File

@ -1,78 +0,0 @@
# 三珠互斥
1. 如果k * 3 大于 n 了,那说明一定没结果,如果没想明白,大家举个例子试试看
2. 分别求出三个红珠子之间的距离
3. 对这三段距离从小到大排序 y1, y2, y3
4. 如果第一段距离y1 小于k说明需要交换 k - y 次, 同理 第二段距离y2 小于k说明需要交换 k - y2 次
5. y1 y2 都调整好了不用计算y3因为 y3是距离最大
```CPP
#include<bits/stdc++.h>
using namespace std;
int main(){
int t;
cin >> t;
int n, k, a1, a2, a3;
vector<int> dis(3);
while (t--) {
cin >> n >> k >> a1 >> a2 >> a3;
if(k * 3 > n){
cout << -1 << endl;
continue;
}
dis[0] = min(abs(a1 - a2), n - abs(a1 - a2));
dis[1] = min(abs(a1 - a3), n - abs(a1 - a3));
dis[2] = min(abs(a3 - a2), n - abs(a3 - a2));
sort(dis.begin(), dis.end());
int result = 0;
if (dis[0] < k) result += (k - dis[0]);
if (dis[1] < k) result += (k - dis[1]);
cout << result << endl;
}
return 0;
}
```
Java代码
```Java
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int t = scanner.nextInt();
while (t-- > 0) {
int n = scanner.nextInt();
int k = scanner.nextInt();
int a1 = scanner.nextInt();
int a2 = scanner.nextInt();
int a3 = scanner.nextInt();
if (k * 3 > n) {
System.out.println(-1);
continue;
}
List<Integer> dis = new ArrayList<>(3);
dis.add(Math.min(Math.abs(a1 - a2), n - Math.abs(a1 - a2)));
dis.add(Math.min(Math.abs(a1 - a3), n - Math.abs(a1 - a3)));
dis.add(Math.min(Math.abs(a3 - a2), n - Math.abs(a3 - a2)));
Collections.sort(dis);
int result = 0;
if (dis.get(0) < k) result += (k - dis.get(0));
if (dis.get(1) < k) result += (k - dis.get(1));
System.out.println(result);
}
}
}
```

View File

@ -1,122 +0,0 @@
# 扑克牌同花顺
首先我们要定义一个结构体,来存放我们的数据
`map<花色,{同一花色牌集合,同一花色的牌对应的牌数量}>`
再遍历 每一个花色下,每一个牌 的数量
代码如下详细注释:
```CPP
#include<bits/stdc++.h>
using namespace std;
string cards[] = {"H","S","D","C"};
typedef long long ll;
struct color
{
set<int> st; // 同一花色 牌的集合
map<int, ll> cnt; // 同一花色 牌对应的数量
};
unordered_map<string, color> umap;
int main() {
int n;
cin >> n;
for (int i = 0; i < n; i++) {
int x, y;
string card;
cin >> x >> y >> card;
umap[card].st.insert(x);
umap[card].cnt[x] += y;
}
ll sum = 0;
// 遍历每一个花色
for (string cardOne : cards) {
color colorOne = umap[cardOne];
// 遍历 同花色 每一个牌
for (int number : colorOne.st) {
ll numberCount = colorOne.cnt[number]; // 获取牌为number的数量是 numberCount
// 统计 number 到 number + 4 都是否有牌用cal 把 number 到number+4 的数量记下来
ll cal = numberCount;
for (int j = number + 1; j <= number + 4; j++) cal = min(cal, colorOne.cnt[j]);
// 统计结果
sum += cal;
// 把统计过的同花顺数量减下去
for (int j = number + 1; j <= number + 4; j++) colorOne.cnt[j] -= cal;
}
}
cout << sum << endl;
}
```
Java代码如下
```Java
import java.util.*;
public class Main {
static String[] cards = {"H", "S", "D", "C"}; // 花色数组
static class Color {
Set<Integer> st; // 同一花色牌的集合
Map<Integer, Long> cnt; // 同一花色牌对应的数量
Color() {
st = new HashSet<>(); // 初始化集合
cnt = new HashMap<>(); // 初始化映射
}
}
static Map<String, Color> umap = new HashMap<>(); // 用于存储每种花色对应的Color对象
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt(); // 读取牌的数量
for (int i = 0; i < n; i++) {
int x = scanner.nextInt(); // 读取牌的值
int y = scanner.nextInt(); // 读取牌的数量
String card = scanner.next(); // 读取牌的花色
umap.putIfAbsent(card, new Color()); // 如果不存在该花色则创建一个新的Color对象
umap.get(card).st.add(x); // 将牌的值加入集合
umap.get(card).cnt.put(x, umap.get(card).cnt.getOrDefault(x, 0L) + y); // 更新牌的数量
}
long sum = 0; // 结果累加器
// 遍历每一种花色
for (String cardOne : cards) {
Color colorOne = umap.getOrDefault(cardOne, new Color()); // 获取对应花色的Color对象
// 遍历同花色的每一张牌
for (int number : colorOne.st) {
long numberCount = colorOne.cnt.get(number); // 获取当前牌的数量
// 计算从当前牌到number+4的最小数量
long cal = numberCount;
for (int j = number + 1; j <= number + 4; j++) {
cal = Math.min(cal, colorOne.cnt.getOrDefault(j, 0L)); // 更新cal为最小值
}
// 将结果累加到sum
sum += cal;
// 将统计过的同花顺数量减去
for (int j = number + 1; j <= number + 4; j++) {
colorOne.cnt.put(j, colorOne.cnt.getOrDefault(j, 0L) - cal);
}
}
}
System.out.println(sum); // 输出结果
}
}
```

View File

@ -1,102 +0,0 @@
# 149. 好数组
贪心思路:
整体思路是移动到中间位置(中位数),一定是 移动次数最小的。
有一个数可以不改变,对数组排序之后, 最小数 和 最大数 一定是移动次数最多的,所以分别保留最小 和 最大的不变。
中间可能有两个位置,所以要计算中间偏前 和 中间偏后的
代码如下:
```CPP
#include<bits/stdc++.h>
using namespace std;
int main() {
int n;
cin >> n;
vector<long> arr(n);
for (int i = 0; i < n; ++i) {
cin >> arr[i];
}
sort(arr.begin(), arr.end());
if (arr[0] == arr[n - 1]) {
cout << 1 << endl;
return 0;
}
long cnt = 0L;
long cnt1 = 0L;
// 如果要保留一个不改变,要不不改最小的,要不不改最大的。
// 取中间偏前的位置
long mid = arr[(n - 2) / 2];
// 不改最大的
for (int i = 0; i < n - 1; i++) {
cnt += abs(arr[i] - mid);
}
// 取中间偏后的位置
mid = arr[n / 2];
// 不改最小的
for (int i = 1; i < n; i++) {
cnt1 += abs(arr[i] - mid);
}
cout << min(cnt, cnt1) << endl;
return 0;
}
```
Java代码如下
```Java
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
long[] arr = new long[n];
for (int i = 0; i < n; ++i) {
arr[i] = scanner.nextLong();
}
Arrays.sort(arr);
if (arr[0] == arr[n - 1]) {
System.out.println(1);
return;
}
long cnt = 0L;
long cnt1 = 0L;
// 如果要保留一个不改变,要不不改最小的,要不不改最大的。
// 取中间偏前的位置
long mid = arr[(n - 2) / 2];
// 不改最大的
for (int i = 0; i < n - 1; i++) {
cnt += Math.abs(arr[i] - mid);
}
// 取中间偏后的位置
mid = arr[n / 2];
// 不改最小的
for (int i = 1; i < n; i++) {
cnt1 += Math.abs(arr[i] - mid);
}
System.out.println(Math.min(cnt, cnt1));
}
}
```

View File

@ -1,66 +0,0 @@
# 150. 极长连续段的权值
动态规划,枚举最后边节点的情况:
```CPP
#include <iostream>
#include <string>
using namespace std;
int main() {
int n;
cin >> n;
string s;
cin >> s;
long long result = 1;
long long a = 1;
for (int i = 1; i < n; ++i) {
// 加上本身长度为1的子串
if (s[i] == s[i - 1]) {
a += 1;
result += a;
// 以最右节点为终点,每个子串的级长连续段都+1再加本身长度为1的子串
} else {
a = a + i + 1;
result += a;
}
}
cout << result << endl;
return 0;
}
```
Java代码如下
```Java
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
String s = scanner.next();
long result = 1;
long a = 1;
for (int i = 1; i < n; ++i) {
// 加上本身长度为1的子串
if (s.charAt(i) == s.charAt(i - 1)) {
a += 1;
result += a;
// 以最右节点为终点,每个子串的级长连续段都+1再加本身长度为1的子串
} else {
a = a + i + 1;
result += a;
}
}
System.out.println(result);
}
}
```

View File

@ -1,127 +0,0 @@
# 151. 手机流畅运行的秘密
[题目链接](https://kamacoder.com/problempage.php?pid=1229)
先运行 能留下电量多的 任务,才能有余电运行其他任务。
任务11:10 ,运行完 能留下 9个电
任务22:12运行完 能留下 10个电
任务33:10运行完 能留下 7个电。
运行顺序: 任务2 -> 任务1 -> 任务3
按照 最低初始电量 - 耗电量,从大到小排序。
计算总电量,需要 从小到大 遍历, 不断取 总电量 + 任务耗电量 与 任务最低初始电量 的最大值。
```CPP
#include<bits/stdc++.h>
using namespace std;
bool cmp(const pair<int,int>& taskA, const pair<int,int>& taskB) {
return (taskA.second - taskA.first) < (taskB.second - taskB.first);
}
int main() {
string str, tmp;
vector<pair<int,int>> tasks;
//处理输入
getline(cin, str);
stringstream ss(str);
while (getline(ss, tmp, ',')) {
int p = tmp.find(":");
string a = tmp.substr(0, p);
string b = tmp.substr(p + 1);
tasks.push_back({stoi(a), stoi(b)});
}
// 按照差值从小到大排序
sort(tasks.begin(), tasks.end(), cmp);
// 收集结果
int result = 0;
for (int i = 0 ; i < tasks.size(); i++) {
result = max(result + tasks[i].first, tasks[i].second);
}
result = result <= 4800 ? result : -1;
cout << result << endl;
}
```
Java版本
```Java
import java.util.*;
import java.util.stream.Collectors;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String str = sc.nextLine();
String[] tasksArray = str.split(",");
List<Pair> tasks = Arrays.stream(tasksArray)
.map(task -> {
String[] parts = task.split(":");
return new Pair(Integer.parseInt(parts[0]), Integer.parseInt(parts[1]));
})
.collect(Collectors.toList());
// 按照差值从小到大排序
Collections.sort(tasks, (taskA, taskB) ->
(taskA.second - taskA.first) - (taskB.second - taskB.first)
);
// 收集结果
int result = 0;
for (Pair task : tasks) {
result = Math.max(result + task.first, task.second);
}
result = result <= 4800 ? result : -1;
System.out.println(result);
}
}
class Pair {
int first;
int second;
Pair(int first, int second) {
this.first = first;
this.second = second;
}
}
```
Python版本
```python
def main():
import sys
input = sys.stdin.read
str = input().strip()
tasks = []
for tmp in str.split(','):
a, b = map(int, tmp.split(':'))
tasks.append((a, b))
# 按照差值从小到大排序
tasks.sort(key=lambda task: task[1] - task[0])
# 收集结果
result = 0
for task in tasks:
result = max(result + task[0], task[1])
result = result if result <= 4800 else -1
print(result)
if __name__ == "__main__":
main()
```

View File

@ -1,121 +0,0 @@
# 152. 小米手机通信校准
[题目链接](https://kamacoder.com/problempage.php?pid=1230)
一道模拟题,但比较考察 代码能力。
遍历去找 里 freq 最近的 freg就好 需要记录刚遍历过的的freg和 loss因为可能有 相邻一样的 freg。
```CPP
#include <bits/stdc++.h>
using namespace std;
int main() {
int freq;
cin >> freq;
string data;
double result = 0;
int last_freg = 0; // 记录上一个 freg
int last_loss = 0; // 记录上一个loss
while(cin >> data) {
int index = data.find(':');
int freg = stoi(data.substr(0, index)); // 获取 freg 和 loss
int loss = stoi(data.substr(index + 1));
// 两遍一样
if(abs(freg - freq) == abs(last_freg - freq)) {
result = (double)(last_loss + loss)/2.0;
} // 否则更新最新的result
else if(abs(freg - freq) < abs(last_freg - freq)){
result = (double)loss;
}
last_freg = freg;
last_loss = loss;
}
printf("%.1lf\n", result);
return 0;
}
```
Java 版本:
```Java
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int freq = sc.nextInt();
sc.nextLine(); // 读取换行符
String inputLine = sc.nextLine(); // 读取包含所有后续输入的行
String[] data = inputLine.split(" "); // 根据空格分割输入
double result = 0;
int lastFreq = 0; // 记录上一个 freg
int lastLoss = 0; // 记录上一个 loss
for (String entry : data) {
int index = entry.indexOf(':');
int freg = Integer.parseInt(entry.substring(0, index)); // 获取 freg 和 loss
int loss = Integer.parseInt(entry.substring(index + 1));
// 两遍一样
if (Math.abs(freg - freq) == Math.abs(lastFreq - freq)) {
result = (double) (lastLoss + loss) / 2.0;
}
// 否则更新最新的 result
else if (Math.abs(freg - freq) < Math.abs(lastFreq - freq)) {
result = (double) loss;
}
lastFreq = freg;
lastLoss = loss;
}
System.out.printf("%.1f\n", result);
sc.close();
}
}
```
Python版本
```python
def main():
import sys
input = sys.stdin.read
data = input().split()
freq = int(data[0])
result = 0
last_freg = 0 # 记录上一个 freg
last_loss = 0 # 记录上一个 loss
for i in range(1, len(data)):
item = data[i]
index = item.find(':')
freg = int(item[:index]) # 获取 freg 和 loss
loss = int(item[index + 1:])
# 两遍一样
if abs(freg - freq) == abs(last_freg - freq):
result = (last_loss + loss) / 2.0
# 否则更新最新的 result
elif abs(freg - freq) < abs(last_freg - freq):
result = loss
last_freg = freg
last_loss = loss
print(f"{result:.1f}")
if __name__ == "__main__":
main()
```

View File

@ -1,95 +0,0 @@
# 权值优势路径计数
[题目链接](https://kamacoder.com/problempage.php?pid=1231)
1、构建二叉树首先根据层序遍历的序列构建二叉树。这可以通过使用队列来实现队列中存储当前节点及其索引确保可以正确地将子节点添加到父节点下。
2、路径遍历使用深度优先搜索DFS遍历所有从根到叶子的路径。在遍历过程中维护一个计数器跟踪当前路径中权值为 1 和权值为 0 的节点的数量。
3、计数满足条件的路径每当到达一个叶子节点时检查当前路径的权值 1 的节点数量是否比权值 0 的节点数量多 1。如果满足递增一个全局计数器。
```CPP
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
struct TreeNode {
int val;
TreeNode *left, *right;
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};
// DFS遍历二叉树并计算满足条件的路径数量
void countPaths(TreeNode* node, int count1, int count0, int& result) {
if (!node) return;
// 更新当前路径中1和0的数量
node->val == 1 ? count1++ : count0++;
// 检查当前节点是否为叶子节点
if (!node->left && !node->right) {
// 检查1的数量是否比0的数量多1
if (count1 == count0 + 1) {
result++;
}
return;
}
// 递归访问左右子节点
countPaths(node->left, count1, count0, result);
countPaths(node->right, count1, count0, result);
}
int main() {
int N;
cin >> N;
vector<int> nums(N);
for (int i = 0; i < N; ++i) {
cin >> nums[i];
}
if (nums.empty()) {
cout << 0 << endl;
return 0;
}
// 根据层序遍历的输入构建二叉树
queue<TreeNode*> q;
TreeNode* root = new TreeNode(nums[0]);
q.push(root);
int index = 1;
while (!q.empty() && index < N) {
TreeNode* node = q.front();
q.pop();
if (index < N && nums[index] != -1) {
node->left = new TreeNode(nums[index]);
q.push(node->left);
}
index++;
if (index < N && nums[index] != -1) {
node->right = new TreeNode(nums[index]);
q.push(node->right);
}
index++;
}
// 计算满足条件的路径数
int result = 0;
countPaths(root, 0, 0, result);
cout << result << endl;
return 0;
}
```

View File

@ -1,68 +0,0 @@
# 序列中位数
[题目链接](https://kamacoder.com/problempage.php?pid=1232)
注意给的数组默认不是有序的!
模拟题排序之后取中位数然后按照b数组 删 a数组中元素再取中位数。
```CPP
#include<bits/stdc++.h>
using namespace std;
// 计算并返回中位数
double findMedian(vector<int>& nums) {
int n = nums.size();
if (n % 2 == 1) {
return nums[n / 2]; // 奇数长度,返回中间的元素
} else {
// 偶数长度,返回中间两个元素的平均值
return (nums[n / 2] + nums[n / 2 - 1]) / 2.0;
}
}
int main(){
int t;
cin >> t;
while(t--){
int n;
cin>> n;
vector<int> a(n);
vector<int> b(n - 1);
for(int i = 0; i < n; i++){
cin >> a[i];
}
for(int i = 0; i < n - 1; i++){
cin >> b[i];
}
vector<int> nums = a;
vector<double> answers;
sort(nums.begin(), nums.end());
// 把中位数放进结果集
answers.push_back(findMedian(nums));
for(int i = 0; i < n - 1; i++){
int target = a[b[i]];
// 删除目标值
nums.erase(find(nums.begin(), nums.end(), target));
// 把中位数放进结果集
answers.push_back(findMedian(nums));
}
for(auto answer : answers){
// 判断是否是整数
if(answer == (int)answer) printf("%d ", (int)answer);
else printf("%.1f ", answer);
}
cout << endl;
}
}
```

View File

@ -1,106 +0,0 @@
# 最小化频率的删除代价
[题目链接](https://kamacoder.com/problempage.php?pid=1233)
计数和排序:
* 使用 map 或 unordered_map 对数组 a 中每个元素出现的次数进行统计。
* 将统计结果存入一个 vector<pair<int, int>>,其中 pair 的第一个元素是元素的出现次数,第二个元素是元素本身。
* 按出现次数从大到小排序这个 vector。
确定最小 f(a)
* 从最大出现次数开始尝试减少 f(a)。为此,从最高频次的元素开始逐步向下考虑较少出现的元素,计算达到更低 f(a) 所需删除的元素数量。
* 使用一个累加器 count 来记录需要删除的元素数量,直到这个数量超过允许的最大删除数量 k 或恰好等于 k。在此过程中尽量使 f(a) 达到最小。
计算达到 f(a) 的代价:
* 计算完成后,需要确定达到最小 f(a) 的确切代价。首先,为每个元素确定在不超过 k 的前提下可以删除的最大数量,以使得 f(a) 最小。
* 对于每个元素,如果它的数量超过了新的 f(a),则计算减少到 f(a) 所需删除的具体元素数,记录下来。
计算具体删除代价:
* 遍历原数组,对于每个需要删除的元素,根据其位置累加删除代价。每删除一个元素,相应地减少其在删除列表中的计数。当某元素需要删除的数量减至 0 时,从删除列表中移除该元素。
```CPP
#include <iostream>
#include <vector>
#include <unordered_map>
#include <algorithm>
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];
}
unordered_map<int, int> umap; // 使用map来统计每个元素的出现频率
for (int i = 0; i < n; ++i) {
umap[a[i]]++; // 统计每个元素的出现次数
}
vector<pair<int, int>> table;
for (auto& pair : umap) {
table.push_back({pair.second, pair.first}); // 将元素和其频率作为一个pair放入table中
}
sort(table.begin(), table.end(), greater<>()); // 将table按照频率从大到小排序
int count = 0; // 用来计算已经删除的元素总数
int minVal = table[0].first; // 从最高频率开始
for (int i = 0; i < table.size(); ++i) {
int freq = table[i].first;
count += (minVal - freq) * i; // 累加删除元素的代价
if (count > k) break; // 如果超过了k停止循环
else if (count == k) {
minVal = freq;
break;
} else minVal = freq;
}
if (count < k) {
int addDel = (k - count) / table.size(); // 如果删除的代价还没达到k计算还可以进一步减少的频率
minVal -= addDel; // 减少相应的频率
}
if (minVal < 0) {
minVal = 0; // 确保最小频率值不小于0
}
unordered_map<int, int> deleteList; // 用来存储需要删除的元素及其数量
for (auto& elem : table) {
int num = elem.first;
int ind = elem.second;
if (num > minVal) {
deleteList[ind] = num - minVal; // 如果元素频率大于最小值,计算需要删除的数量
} else {
break;
}
}
int cost = 0; // 计算总的删除代价
for (int i = 0; i < n; ++i) {
if (deleteList.find(a[i]) != deleteList.end()) {
cost += i + 1; // 删除的代价是元素的索引+1
deleteList[a[i]]--; // 删除一个元素
if (deleteList[a[i]] == 0) {
deleteList.erase(a[i]); // 如果元素已经全部删除,从列表中移除
if (deleteList.empty()) {
break; // 如果没有元素需要删除了,结束循环
}
}
}
}
cout << minVal << " " << cost << endl;
return 0;
}
```

View File

@ -1,68 +0,0 @@
# 勇敢牛牛战斗序列
[题目链接](https://kamacoder.com/problempage.php?pid=1234)
贪心思路,对数组从小到大排序之后,先取最右边,再取最左边,循环反复。
```CPP
#include<bits/stdc++.h>
using namespace std;
int main() {
int n;
cin >> n;
vector<int> a(n); // 使用 vector 存储整数数组
for (int i = 0; i < n; i++) {
cin >> a[i]; // 读取数组
}
sort(a.begin(), a.end()); // 对数组进行排序
long long ans = 0; // 使用 long long 存储结果,以防溢出
int cur = 0;
int left = 0, right = n - 1;
while (left <= right) {
if (cur < a[right]) {
ans += a[right] - cur;
}
cur = a[left];
right--;
left++;
}
cout << ans << endl; // 输出结果
return 0;
}
```
```Java
import java.util.Arrays;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int[] a = new int[n];
for (int i = 0; i < n; i++) {
a[i] = sc.nextInt();
}
Arrays.sort(a);
long ans = 0;
int cur = 0;
int left = 0, right = a.length - 1;
while (left <= right) {
if (cur < a[right]) {
ans = ans + a[right] - cur;
}
cur = a[left];
right--;
left++;
}
System.out.println(ans);
}
}
```

View File

@ -1,59 +0,0 @@
# 最大化密码复杂度
[题目链接](https://kamacoder.com/problempage.php?pid=1235)
注意**边界处理**,对于字符串的首尾位置,需要特别处理,因为它们只有一个相邻字符。
* 遍历字符串 s寻找 '?' 字符。
* 对于每个 '?' 字符,选择一个字符填充,使其与前后字符都不同。这样做的目的是最大化密码的复杂度,即尽可能使相邻的字符不同。
* 如果 '?' 是第一个或最后一个字符,或者无法找到与前后都不同的字符,选择与前一个或后一个字符不同的字符。
```CPP
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
int n, m;
string s;
cin >> n >> m >> s;
if (n == 1) {
cout << 0 << endl;
return 0;
}
// 统一处理包括左右字符的情况
for (int i = 0; i < n; ++i) {
if (s[i] == '?') {
bool found = false;
for (char j = 'a'; j < 'a' + m; ++j) {
// 避免第一个字符 和 最后一个字符,因为两个字符只有一个相邻字符,没有左右相邻字符
if ((i == 0 || s[i - 1] != j) && (i == n - 1 || s[i + 1] != j)) {
s[i] = j;
found = true;
break;
}
}
// 如果没有找到合适的字符,就和附近字符保持一致
if (!found) {
if (i > 0) s[i] = s[i - 1];
else s[i] = s[i + 1];
}
}
}
// 计算结果
int result = 0;
for (int i = 0; i < n - 1; ++i) {
if (s[i] != s[i + 1]) result++;
}
cout << result << endl;
return 0;
}
```

View File

@ -1,50 +0,0 @@
# 同余方程
题目链接https://kamacoder.com/problempage.php?pid=1236
我们需要求出满足以下条件的最小正整数 x`ax≡1 (mod b)`
这意味着我们需要找到 x 使得 ax 除以 b 的余数是 1。这个问题实际上是一个典型的 模反元素 问题。
解题思路:
* 为了求出最小的 x我们可以使用 扩展欧几里得算法 来求出 a 对模 b 的逆元。
* 这个算法能够求解 ax + by = gcd(a, b) 的一组整数解 (x, y),而在 gcd(a, b) = 1 的情况下x 即为所求的模逆元。
* 扩展欧几里得算法:扩展欧几里得算法可以通过递归或者迭代的方式实现。
下面给出C++代码实现:
```CPP
#include <iostream>
using namespace std;
// 扩展欧几里得:计算 ax + by = gcd(a, b) 的解
long long extended_gcd(long long a, long long b, long long &x, long long &y) {
if (b == 0) {
x = 1;
y = 0;
return a;
}
long long x1, y1;
long long gcd = extended_gcd(b, a % b, x1, y1);
x = y1;
y = x1 - (a / b) * y1;
return gcd;
}
int main() {
long long a, b;
cin >> a >> b;
long long x, y;
long long gcd = extended_gcd(a, b, x, y);
// 由于我们只需要模 b 的正整数解,所以我们要保证 x 是正数
x = (x % b + b) % b;
cout << x << endl;
return 0;
}
```

View File

@ -1,62 +0,0 @@
# 大整数乘法
题目链接https://kamacoder.com/problempage.php?pid=1237
思路:
我们可以使用模拟手算乘法的方法,即「逐位相乘累加」,对于每一位的乘法结果,我们将其加到相应的结果位置上。最终将累加的结果输出。
具体步骤:
* 初始化结果数组:结果数组的长度应该是两个数字长度之和,因为最大长度的结果不会超过这个长度。
* 逐位相乘:从右往左遍历两个字符串的每一位,逐位相乘,并加到结果数组的相应位置。
* 处理进位在每一步累加之后处理进位保证每个位置的值小于10。
将结果数组转化为字符串:从结果数组的最高位开始,忽略前导零,然后将数组转化为字符串。
```CPP
#include <iostream>
#include <vector>
#include <string>
using namespace std;
string multiply(string num1, string num2) {
int len1 = num1.size();
int len2 = num2.size();
vector<int> result(len1 + len2, 0);
// 逐位相乘
for (int i = len1 - 1; i >= 0; i--) {
for (int j = len2 - 1; j >= 0; j--) {
int mul = (num1[i] - '0') * (num2[j] - '0');
int sum = mul + result[i + j + 1];
result[i + j + 1] = sum % 10;
result[i + j] += sum / 10;
}
}
// 将结果转换为字符串,跳过前导零
string product;
for (int num : result) {
if (!(product.empty() && num == 0)) { // 跳过前导零
product.push_back(num + '0');
}
}
return product.empty() ? "0" : product;
}
int main() {
string num1, num2;
cin >> num1 >> num2;
string result = multiply(num1, num2);
cout << result << endl;
return 0;
}
```

View File

@ -1,88 +0,0 @@
# 二维平面上的折线段
题目链接https://kamacoder.com/problempage.php?pid=1238
这个问题要求我们在一条折线段上,根据移动的固定距离 s 进行标记点的计算。
为了实现这一点,我们需要对折线段进行分段处理,并根据每段的长度来确定标记点的位置。
解题思路:
1. 输入与初步处理:
* 首先,读取所有点的坐标。
* 计算每一段折线的长度,并逐段累积总长度。
2. 确定标记点:
* 从起点开始,每次沿着折线段前进 s 的距离,直到到达终点。
* 对于每个标记点,根据当前段的起点和终点,计算出该点的精确坐标。
3. 输出所有标记点的坐标,格式为 x, y。
```CPP
#include <iostream>
#include <vector>
#include <cmath>
#include <iomanip>
using namespace std;
// 定义一个点的结构体
struct Point {
double x, y;
};
// 计算两点之间的距离
double distance(const Point& a, const Point& b) {
return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}
int main() {
int n;
cin >> n;
vector<Point> points(n);
for (int i = 0; i < n; i++) {
cin >> points[i].x >> points[i].y;
}
double s;
cin >> s;
double total_length = 0.0;
vector<double> segment_lengths(n - 1);
// 计算每段长度和总长度
for (int i = 0; i < n - 1; i++) {
segment_lengths[i] = distance(points[i], points[i + 1]);
total_length += segment_lengths[i];
}
// 从起点开始标记
Point current_point = points[0];
double accumulated_distance = 0.0;
cout << fixed << setprecision(5);
cout << current_point.x << ", " << current_point.y << endl;
while (accumulated_distance + s <= total_length) {
accumulated_distance += s;
double remaining_distance = accumulated_distance;
for (int i = 0; i < n - 1; i++) {
if (remaining_distance <= segment_lengths[i]) {
double ratio = remaining_distance / segment_lengths[i];
double new_x = points[i].x + ratio * (points[i + 1].x - points[i].x);
double new_y = points[i].y + ratio * (points[i + 1].y - points[i].y);
current_point = {new_x, new_y};
cout << current_point.x << ", " << current_point.y << endl;
break;
} else {
remaining_distance -= segment_lengths[i];
}
}
}
return 0;
}
```

View File

@ -1,61 +0,0 @@
# 讨厌鬼的组合帖子
[题目链接](https://kamacoder.com/problempage.php?pid=1239)
这个问题本质上是要找到两个数组的子集,使得这两个子集之间的差的绝对值最大。
问题可以简化为寻找两个数列之间最大可能的差的绝对值。
贪心思路如下:
计算差异,首先,我们可以计算每个帖子的点赞数和点踩数的差值 d[i] = a[i] - b[i]。这样问题就转化为选择这些差值的一个子集,使得子集中所有元素的和的绝对值最大。
遍历可能性,要使得一个数的绝对值尽可能大,可以尝试最大化这个数,或者最小化这个数(使其尽可能小于零)。我们可以分别尝试将所有正的差值加在一起,以及将所有负的差值加在一起。
计算最大吸引度:
* 将所有正的差值求和得到一个总和。
* 将所有负的差值求和得到另一个总和。
* 最后,吸引度即为这两个总和的绝对值中的较大者。
```CPP
#include <iostream>
#include <vector>
#include <cmath>
using namespace std;
int main() {
int n;
cin >> n;
vector<int> a(n), b(n);
for (int i = 0; i < n; ++i) {
cin >> a[i];
}
for (int i = 0; i < n; ++i) {
cin >> b[i];
}
long long positive_sum = 0;
long long negative_sum = 0;
for (int i = 0; i < n; ++i) {
int difference = a[i] - b[i];
if (difference > 0) {
positive_sum += difference;
} else if (difference < 0) {
negative_sum += difference;
}
}
// 最大吸引度是正总和或负总和的绝对值中的较大者
cout << max(abs(positive_sum), abs(negative_sum)) << endl;
return 0;
}
```

View File

@ -1,88 +0,0 @@
# 优秀数组
[题目链接](https://kamacoder.com/problempage.php?pid=1241)
## 解题思路
1、初始分析
- 给定一个排列 `p`,我们首先构建一个 `pos` 数组,使得 `pos[i]` 表示 `i` 在排列 `p` 中的位置。
- 我们需要判断数组 `a` 是否是一个优秀数组,即 `pos[a[i]] < pos[a[i+1]] <= pos[a[i]] + d` 对于所有 `i` 都成立。
- 我们的目标是通过最少的相邻元素交换,使得数组 `a` 不再是一个优秀数组。
2、思路
- 要使数组 `a` 不再是优秀数组,我们只需要打破条件 `pos[a[i]] < pos[a[i+1]] <= pos[a[i]] + d` 中的某一个。
- 一种简单的做法是让 `pos[a[i]]``pos[a[i+1]]` 之间的距离超过 `d`,或者直接让 `pos[a[i]] >= pos[a[i+1]]`
3、具体方法
- 只需要考虑 `a` 中相邻元素的顺序,并判断如何交换 `p` 中相邻元素使得其顺序被打破。
- 假设我们需要在 `p` 中交换某些元素来实现上述目标,那么最小的交换次数是将 `a[i]``a[i+1]` 的位置交换。
- 如果 `pos[a[i]] + 1 == pos[a[i+1]]`,则需要一步交换。
4、特别情况
- 还需要考虑,如果通过交换相邻元素无法解决问题的情况。比如 `pos[a[i+1]]` 的位置无法移到 `pos[a[i]]` 的前面或超过 `d`
C++代码如下:
```cpp
#include <iostream>
#include <vector>
#include <algorithm>
#include <climits>
using namespace std;
int main() {
int n, m, d;
cin >> n >> m >> d;
vector<int> p(n + 1);
vector<int> pos(n + 1);
// 读取排列 p并构建位置数组 pos
for (int i = 1; i <= n; i++) {
cin >> p[i];
pos[p[i]] = i;
}
vector<int> a(m);
for (int i = 0; i < m; i++) {
cin >> a[i];
}
int min_operations = INT_MAX;
// 遍历数组 a 的相邻元素
for (int i = 0; i < m - 1; i++) {
int current_pos = pos[a[i]];
int next_pos = pos[a[i + 1]];
// 检查 pos[a[i]] < pos[a[i+1]] <= pos[a[i]] + d 是否成立
if (current_pos < next_pos && next_pos <= current_pos + d) {
// 计算需要的最少操作次数
int distance = next_pos - current_pos;
// Case 1: 交换 current_pos 和 next_pos
min_operations = min(min_operations, distance);
// Case 2: 如果 next_pos + d <= n考虑使 pos[a[i+1]] 超过 pos[a[i]] + d
if (current_pos + d + 1 <= n) {
min_operations = min(min_operations, d + 1 - distance);
}
} else {
min_operations = 0;
}
}
cout << min_operations << endl;
return 0;
}
```
时间复杂度为 O(m)

View File

@ -1,81 +0,0 @@
# 升序数组
[题目链接](https://kamacoder.com/problempage.php?pid=1241)
## 解题思路
贪心思路
- **计算相邻元素差值**
- 对于数组 `a`,计算每对相邻元素的差值 `diff[i] = a[i+1] - a[i]`
- 如果 `diff[i]` 为负数,意味着 `a[i+1]``a[i]` 小或相等,需要通过操作使 `a[i+1]` 变大。
- **确定最小操作次数**
- 计算所有相邻元素中的最小差值 `minDifference`,即 `minDifference = min(diff[i])`
- 如果 `minDifference` 为负数或零,则需要进行 `-minDifference + 1` 次操作,使得 `a[i+1]` 大于 `a[i]`,从而使数组严格递增。
- **实现细节**
- 遍历数组的每对相邻元素,找出最小的差值。
- 根据最小差值,计算出最少的操作次数。
```CPP
#include <iostream>
#include <vector>
#include <climits>
using namespace std;
int main()
{
int n;
cin >> n;
vector<int> arr(n); // 用于存储输入数组
vector<int> differences; // 用于存储相邻元素的差值
for(int i = 0; i < n; i++) {
cin >> arr[i];
if(i > 0) differences.push_back(arr[i] - arr[i - 1]);
}
int minDifference = INT_MAX;
// 寻找最小的差值
for(int diff : differences) {
if(diff < minDifference) {
minDifference = diff;
}
}
// 如果最小差值是负数或零,计算所需的操作次数
int minOperations = max(0, -minDifference + 1);
cout << minOperations << endl;
return 0;
}
```
关于 `-minDifference + 1` 为什么要 + 1 解释:
对于数组 `a` 中相邻的两个元素 `a[i]` 和 `a[i+1]`,我们计算它们的差值 `diff = a[i+1] - a[i]`。
- **目标**:要使 `a[i] < a[i+1]`,需要 `diff > 0`。
- 如果 `diff < 0`,说明 `a[i+1]` 比 `a[i]` 小,这时候 `a` 不是严格递增的。
- 如果 `diff = 0`,说明 `a[i+1]` 和 `a[i]` 相等,这时也不满足严格递增。
解释 `-minDifference + 1`
1. **当 `minDifference < 0` 时**
- 假设 `minDifference` 是所有相邻差值中的最小值,并且它是一个负数。
- 例如,`minDifference = -3`,表示 `a[i+1] - a[i] = -3`,也就是 `a[i+1]` 比 `a[i]` 小 `3`。
- 要让 `a[i+1] > a[i]`,我们至少需要使 `a[i+1] - a[i]` 从 `-3` 增加到 `1`。因此需要增加 `4`,即 `(-(-3)) + 1 = 3 + 1 = 4` 次操作。
2. **当 `minDifference = 0` 时**
- `minDifference` 等于 `0`,表示 `a[i+1] - a[i] = 0`,即 `a[i+1]` 和 `a[i]` 相等。
- 为了使 `a[i+1] > a[i]`,我们至少需要进行一次操作,使得 `a[i+1]` 大于 `a[i]`。

View File

@ -1,82 +0,0 @@
# 最大字典序无重复串
[题目链接](https://kamacoder.com/problempage.php?pid=1243)
## 解题思路
贪心思路
为了保证字典序最大,我们优先放置字母 `b`,然后再放置字母 `a`。在放置字符时,我们还需注意不能超过连续 `k` 次相同字符:
- 如果当前已经连续放置了 `k` 次相同字符,必须切换到另一个字符。
- 每次放置字符后,相应的字符数量减少,同时更新当前字符的连续计数。
实现步骤:
- **初始化**:根据输入的 `x`, `y`, `k` 值,检查是否有可能构造出满足条件的字符串。初始化结果字符串的大小,并设置初始计数器。
- **循环放置字符**
- 优先放置字符 `b`,如果 `b` 的数量已经足够,或者已经放置了 `k` 次字符 `b`,则放置字符 `a`
- 如果已经放置了 `k` 次相同字符,则强制切换到另一个字符。
C++代码如下:
```CPP
#include<iostream>
#include<string>
using namespace std;
int main() {
int countA, countB, maxRepeat;
cin >> countA >> countB >> maxRepeat;
// 检查是否有可能生成满足条件的字符串
if (countA > (countB + 1) * maxRepeat || countB > (countA + 1) * maxRepeat) {
cout << -1 << endl;
return 0;
}
string result(countA + countB, ' '); // 预先分配字符串大小
int currentA = 0, currentB = 0; // 当前连续 'a' 和 'b' 的计数
int pos = 0; // 当前填充位置
while (countA > 0 || countB > 0) {
// 当可以继续添加 'a' 或 'b' 且没有超过最大连续限制时
if (currentA < maxRepeat && currentB < maxRepeat) {
if (countA <= countB * maxRepeat) {
result[pos++] = 'b';
countB--;
currentB++;
currentA = 0;
} else {
result[pos++] = 'a';
countA--;
currentA++;
currentB = 0;
}
}
// 当当前字符达到最大连续限制时,切换到另一个字符
if (currentA == maxRepeat || currentB == maxRepeat) {
if (result[pos - 1] == 'a') {
result[pos++] = 'b';
countB--;
currentB = 1;
currentA = 0;
} else {
result[pos++] = 'a';
countA--;
currentA = 1;
currentB = 0;
}
}
}
cout << result << endl;
return 0;
}
```
时间复杂度O(n)