最长同余子数组
思路:首先,我们需要找到同余的连续子序列,可以考虑枚举每个位置作为子序列的起点,然后再尝试向后扩展,直到无法继续扩展。
对于每个起点,我们可以通过遍历整个子序列,计算该子序列中所有元素模 M 的余数是否相同,来判断该子序列是否是同余的。如果是同余的,就记录下当前子序列的长度,并更新最长同余子序列的长度。
代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 2010;
int n;
long long a[N];
int main()
{
cin >> n;
for (int i = 0; i < n; i ++ ) cin >> a[i];
int res = 0;
for (int i = 0; i < n; i ++ )
for (int j = i + 1; j < n; j ++ )
{
int len = j - i + 1;
if (len <= res) continue; // 如果当前长度已经小于等于已有的最长同余子序列长度,就没必要继续计算了
bool flag = true;
for (int k = i + 1; k <= j; k ++ )
if (a[k] % len != a[k - 1] % len) // 如果有元素的余数不同,就说明当前子序列不同余
{
flag = false;
break;
}
if (flag) res = len; // 如果当前子序列同余,就更新最长同余子序列长度
}
cout << res << endl;
return 0;
}
上帝的集合
思路:
这道题是一道典型的使用 STL 的题目,题目中需要维护一个空集合,支持插入、加上一个数、查找并删除最小值等操作。
这里我们可以使用 STL 中的优先队列来解决,并且提供了 lower_bound() 函数,可以在 O(log n) 的时间复杂度内查找第一个大于等于指定元素的迭代器,也就是可以用来查找最小值。
代码:
#include <bits/stdc++.h>
using namespace std;
int main()
{
int n;
priority_queue<long long, vector<long long>, greater<long long>> q;
cin >> n;
while (n--) {
int op;
cin >> op;
if (op == 1) {
long long x;
cin >> x;
q.push(x);
} else if (op == 2) {
long long x;
cin >> x;
priority_queue<long long, vector<long long>, greater<long long>> tmp;
while (!q.empty()) {
long long y = q.top() + x;
tmp.push(y);
q.pop();
}
swap(q, tmp);
} else if (op == 3) {
cout << q.top() << endl;
q.pop();
}
}
return 0;
}
简单的异或问题
思路:
首先,由于我们需要求出选择数的个数 k,因此需要用一个 cnt 变量来记录已经选择的数的个数,初始值为 0。
然后,我们遍历整数的每一位(从低位到高位),如果该位为 1,就说明需要选择这一位,因此将 cnt 自增 1。
最后,如果 cnt 大于 m,那么无法选择足够的数使异或和为 n,此时输出 -1;否则输出 k 的最大值,即 m 减去 cnt。
代码:
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long LL;
int main()
{
LL n, m;
cin >> n >> m;
LL cnt = 0;
for (int i = 0; i < m; i ++)
if (n & (1ll << i))
cnt ++;
if (cnt > m) cout << -1 << endl;
else cout << m - cnt << endl;
return 0;
}
真假字符串
思路:
首先对两个字符串进行逐位比较,如果发现不同的字符,就记录下来,并统计不同字符的个数。
如果不同字符的个数等于 0 或等于 1,那么就无法通过删除一个字符的操作来使得两个字符串相同,输出 0。
如果不同字符的个数等于 2,那么需要检查这两个不同的字符是否互为交换字符,即检查在这两个字符上交换一下是否能够使得两个字符串相同。如果可以,则输出 1,否则输出 0。
如果不同字符的个数大于 2,那么就无法通过删除一个字符的操作来使得两个字符串相同,输出 0。
代码:
#include <bits/stdc++.h>
using namespace std;
int main()
{
string s1, s2;
cin >> s1 >> s2;
int len = s1.length();
int cnt = 0;
int pos1 = -1, pos2 = -1;
for (int i = 0; i < len; i++)
{
if (s1[i] != s2[i])
{
cnt++;
if (pos1 == -1)
{
pos1 = i;
}
else if (pos2 == -1)
{
pos2 = i;
}
else
{
cout << 0 << endl;
return 0;
}
}
}
if (cnt == 0)
{
cout << 0 << endl;
}
else if (cnt == 1)
{
cout << 0 << endl;
}
else if (cnt == 2 && s1[pos1] == s2[pos2] && s1[pos2] == s2[pos1])
{
cout << 1 << endl;
}
else
{
cout << 0 << endl;
}
return 0;
}
弗拉德和糖果 II
思路:
可以将题目理解为在一个长度为n的序列中选择若干个不相邻的数,使得这些数的和最大。因为选择的数不能相邻,所以对于任何一个数i,其前一个数只能是i-2或i-3,因此可以得到状态转移方程: dp[i] = max(dp[i-2], dp[i-3]) + nums[i] 其中dp[i]表示到第i个数时不相邻的数的最大和,nums[i]表示第i个数的大小。由于起始状态不同,因此最终答案是max(dp[n-1], dp[n-2])。
代码:
#include <iostream>
#include <cstring>
using namespace std;
const int N = 5e6 + 10;
int a[N], f[N][2];
int main()
{
int n;
cin >> n;
for (int i = 1; i <= n; i ++ ) cin >> a[i];
f[1][1] = a[1];
for (int i = 2; i <= n; i ++ )
{
f[i][0] = max(f[i - 1][0], f[i - 1][1]);
f[i][1] = f[i - 1][0] + a[i];
}
if (f[n][0] == f[n][1]) cout << "YES" << endl;
else cout << "NO" << endl;
return 0;
}
排队
思路:
可以直接枚举所有可能的排列,然后判断是否符合给定条件即可。
代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 100010;
int n, m;
int h[N], e[N], ne[N], idx;
int d[N], q[N];
void add(int a, int b)
{
e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
d[b] ++ ;
}
bool topsort()
{
int hh = 0, tt = -1;
for (int i = 1; i <= n; i ++ )
if (!d[i])
q[ ++ tt] = i;
while (hh <= tt)
{
int t = q[hh ++ ];
for (int i = h[t]; ~i; i = ne[i])
{
int j = e[i];
if (-- d[j] == 0) q[ ++ tt] = j;
}
}
return tt == n - 1;
}
int main()
{
cin >> n >> m;
memset(h, -1, sizeof h);
while (m -- )
{
int a, b;
cin >> a >> b;
add(a, b);
}
if (topsort()) puts("Yes");
else puts("No");
return 0;
}
吃糖果
思路:
这个问题可以用双指针的思路来解决。
我们可以让小明从左边开始吃糖果,小红从右边开始吃糖果,两个人不断向中间移动,直到两个人吃掉的糖果重量相等或者已经无法继续移动。
具体的,我们可以用两个指针 l 和 r,分别指向糖果序列的开头和结尾。然后每次判断小明和小红是否吃掉的糖果重量相等,如果相等,就更新最大值,然后两个指针都往中间移动一格。如果小明吃掉的糖果重量比小红少,就让小明继续往右边移动一格,否则让小红往左边移动一格。一直这样移动,直到两个指针相遇或者小明和小红已经无法移动为止。
最后输出最大值即可。
代码:
#include<bits/stdc++.h>
using namespace std;
int main() {
int n;
cin >> n;
vector<int> w(n);
for (int i = 0; i < n; ++i) {
cin >> w[i];
}
int l = 0, r = n - 1;
int sum_l = w[l], sum_r = w[r];
int ans = 0;
while (l < r) {
if (sum_l == sum_r) {
ans = max(ans, sum_l);
++l;
--r;
sum_l += w[l];
sum_r += w[r];
} else if (sum_l < sum_r) {
++l;
sum_l += w[l];
} else {
--r;
sum_r += w[r];
}
}
cout << ans << endl;
return 0;
}
模拟输出受限制的双端队列
思路:
本题可以使用搜索的方法进行求解,对于每个状态,枚举下一个数出队的位置,时间复杂度为 O(2^nn^2),可以通过本题。
代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 8;
int n;
char s[N];
int l[N], r[N];
int dfs(int depth) {
if (depth == n) return 1;
int res = 0;
for (int i = 0; i < n; i++) {
if (l[i] == -1 && r[i] == -1) {
l[i] = r[i] = depth;
res += dfs(depth + 1);
l[i] = r[i] = -1;
} else if (l[i] == -1) {
bool flag = true;
for (int j = r[i] + 1; j < depth; j++) {
if (s[j] == s[i]) {
flag = false;
break;
}
}
if (flag) {
l[i] = depth;
res += dfs(depth + 1);
l[i] = -1;
}
} else if (r[i] == -1) {
bool flag = true;
for (int j = l[i] - 1; j >= 0; j--) {
if (s[j] == s[i]) {
flag = false;
break;
}
}
if (flag) {
r[i] = depth;
res += dfs(depth + 1);
r[i] = -1;
}
}
}
return res;
}
int main() {
scanf("%d%s", &n, s);
memset(l, -1, sizeof l);
memset(r, -1, sizeof r);
sort(s, s + n);
printf("%d\n", dfs(0));
return 0;
}