2023 ACM week-6

最长同余子数组

思路:首先,我们需要找到同余的连续子序列,可以考虑枚举每个位置作为子序列的起点,然后再尝试向后扩展,直到无法继续扩展。

对于每个起点,我们可以通过遍历整个子序列,计算该子序列中所有元素模 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;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值