Cocoicobird
热爱永远可以成为你继续下去的理由

牛客刷题-Day31

今日刷题:\(1061-1065\)

1061 NC15805 字典序最大的子序列

题目描述

给定字符串 \(s\)\(s\) 只包含小写字母,请求出字典序最大的子序列。
子序列:https://en.wikipedia.org/wiki/Subsequence
字典序:https://en.wikipedia.org/wiki/Lexicographical_order

输入描述

一行一个字符串 \(s(1 <= |s| <= 100,000)\)

输出描述

字典序最大的子序列。

示例1

输入

ababba

输出

bbba
示例2

输入

abbcbccacbbcbaaba

输出

cccccbba
解题思路

按照字典序下降的顺序,依次选择字母,且所选字母的索引依次递增。

C++ 代码
#include <bits/stdc++.h>
using namespace std;
const int N = 100010, M = 26;

string S;
vector<int> pos[M];

int main() {
    cin >> S;
    for (int i = 0; S[i]; i++)
        pos[S[i] - 'a'].push_back(i);
    int last = -1;
    for (int i = M - 1; i >= 0; i--) { // 字母从 z 到 a
        if (pos[i].size() > 0) { // 存在该字母
            if (last == -1) {
                for (int j = 0; j < pos[i].size(); j++)
                    cout << (char) (i + 'a');
            } else {
                int st = -1;
                for (int j = 0; j < pos[i].size(); j++)
                    if (pos[i][j] > last) {
                        st = j;
                        break;
                    }
                if (st != -1) {
                    for (int j = st; j < pos[i].size(); j++)
                        cout << (char) (i + 'a');
                }
            }
            last = max(last, pos[i][pos[i].size() - 1]);
        }
    }
    return 0;
}

1062 NC19784 Shopping

题目描述

你要买 \(n\) 件物品,其中有一些是凳子。
商场正在举行促销活动,如果购物车中有至少一个凳子,那么你可以半价购买这个购物车中最贵的一个物品。
你有 \(m\) 辆购物车,请最小化你的花费。

输入描述

第一行一个整数 \(t\) 表示数据组数 \((1 ≤ t ≤ 100)\)
每组数据第一行两个整数 \(n,m (1 ≤ n,m ≤ 1000)\),接下来 \(n\) 行每行两个整数 \(a_i,b_i\),分别表示第i件物品的价格以及它是否是凳子 \((1 ≤ a_i ≤ 10^5, 0 ≤ b_i ≤ 1)\)

输出描述

每组数据输出一行一个实数表示最小花费,保留一位小数。

示例

输入

2
5 1
1 0
2 1
3 1
4 0
5 0
5 10
1 0
2 1
3 1
4 0
5 0

输出

12.5
10.5
解题思路

统计凳子的个数,凳子的个数就是可以半价购买的次数(且不超过购物车数量),然后从高价开始购买。如果还有半价购买次数,则半价购买,半价购买次数减一;否则全家购买。
证明:假设两个商品价格为 \(x\)\(y\),且 \(x<y\),只有一次打折机会,则有两种情况,\(x+y/2\)\(x/2+y\),则 \((x/2+y)-(x+y/2)=y/2-x/2>0\),因此需要按照价格降序。

C++ 代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1010;

int T;
int a[N], b[N];

int main() {
    scanf("%d", &T);
    while (T--) {
        int n, m;
        scanf("%d%d", &n, &m);
        int cnt = 0;
        for(int i = 1; i <= n; i++) {
            scanf("%d%d", &a[i], &b[i]);
            if (b[i]) cnt++;
        }
        sort(a + 1, a + n + 1);
        double ans = 0;
        cnt = min(m, cnt);
        for(int i = n; i >= 1; i--) {
            if (cnt <= 0) ans += a[i];
            else ans += 0.5 * a[i];
            --cnt;
        }
        printf("%.1f\n",ans);
    }
    return 0;
}

1063 NC204859 组队

题目描述

你的团队中有 \(n\) 个人,每个人有一个能力值 \(a_i\),现在需要选择若干个人组成一个团队去参加比赛,由于比赛的规则限制,一个团队里面任意两个人能力的差值必须要小于等于 \(k\),为了让更多的人有参加比赛的机会,你最多能选择多少个人参加比赛?

输入描述

第一行一个整数 \(T\),表示案例组数。
每个案例有两行:
第一行两个正整数 \(n,k\),表示人的数量。
第二行n个以空格分隔的整数 \(a_i\),表示每个人的能力值。

输出描述

每个案例输出一行,表示可以参加比赛的最多人数。

示例

输入

1
5 3
8 3 5 1 6

输出

3

说明:选择能力值为 \(3,5,6\) 或者 \(5,6,8\)

备注

\(T≤10\)
\(1≤n≤2e^5,1≤k≤1e^9\)
\(1<=a_i<=1e^9\)

解题思路

因为需要 团队里面任意两个人能力的差值必须要小于等于 \(k\),则最大值与最小值之差需要小于等于 \(k\),则按照能力值升序排序,找出长度最长的连续序列且该序列的最后一个数与第一个数的差值小于等于 \(k\)

C++ 代码
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;

int T, a[N];

int main() {
    scanf("%d", &T);
    while (T--) {
        int n, k;
        scanf("%d%d", &n, &k);
        for (int i = 1; i <= n; i++)
            scanf("%d", &a[i]);
        queue<int> q;
        sort(a + 1, a + n + 1);
        int ans = 0;
        for (int i = 1; i <= n; i++) {
            q.push(a[i]);
            if (q.back() - q.front() > k)
                q.pop();
            if (q.size() > ans)
                ans = q.size();
        }
        printf("%d\n", ans);
    }
    return 0;
}

1064 NC201628 纸牌游戏

题目描述

今天你与你的挚友玩一种纸牌游戏,每一回合两人各出一张牌,如果你的牌点数大小比对方小,则你不得分,否则你的得分为两张卡牌点数差值的绝对值。
由于你有读心术,你已经知道了对方接下来要出的 \(n\) 张牌,\(a_1,a_2,a_3 ... a_n\)
你手上也有 \(n\) 张牌 \(b_1,b_2,b3 ... b_n\),问你如何安排这 \(n\) 张牌使你得的分数最大?

输入描述

第一行一个数字 \(n\),代表\(n\)张牌。(\(1<=n<=2e^5\)
第二行 \(n\) 个数字 \(a_1,a_2,a_3 ... a_n\),代表你的挚友的出牌的点数大小。(\(1<=a_i<=1e^{18}\)
第二行 \(n\) 个数字 \(b_1,b_2,b3 ... b_n\),代表你的牌的点数大小。(\(1<=b_i<=1e^{18}\)

输出描述

输出你得的最大分数。

示例

输入

1
1
2

输出

1
解题思路

将序列 \(a\)\(b\) 分别升序排序,然后将 \(a\) 的最小值与 \(b\) 的最大值(以此类推)进行配对,计算 \(b_j - a_i\) 的正数贡献总和。
证明:要最大化 \(\sum \max(0, b_{\sigma(i)} - a_i)\)。假设有两个数 \(a_1 < a_2\) 和两个数 \(b_1 < b_2\)。可能的配对方式有两种:
情况 A:同向配对(小配小,大配大)
配对为 \((a_1, b_1)\)\((a_2, b_2)\)
收益 \(W_A = \max(0, b_1 - a_1) + \max(0, b_2 - a_2)\)
情况 B:反向配对(小配大,大配小)
配对为 \((a_1, b_2)\)\((a_2, b_1)\)
收益 \(W_B = \max(0, b_2 - a_1) + \max(0, b_1 - a_2)\)

由于 \(a_1\) 是最小的,它最容易从 \(B\) 中获得正向贡献。在贪心策略中,我们将最小的 \(a\) 许配给 最大的 \(b\)
通过数值分布来观察:

  • 如果所有 \(b > a\),则 \(W_A = (b_1 - a_1) + (b_2 - a_2) = (b_1 + b_2) - (a_1 + a_2)\)
  • 同理 \(W_B = (b_2 - a_1) + (b_1 - a_2) = (b_1 + b_2) - (a_1 + a_2)\)
  • 结论:在这种情况下,两种配对方式结果一致。

当数值交错时(例如 \(a_1 < b_1 < a_2 < b_2\)):

  • \(W_A = (b_1 - a_1) + (b_2 - a_2)\)
  • \(W_B = (b_2 - a_1) + 0\) (因为 \(b_1 - a_2 < 0\)
  • 此时 \(W_B - W_A = (b_2 - a_1) - (b_1 - a_1 + b_2 - a_2) = a_2 - b_1 > 0\)\(B\) 更优。

因此,对于两个序列,某两个元素按照情况 \(B\) 反向配对,收益更高,因此每次交换,就是两个序列每个元素反向配对。

C++ 代码
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;

int n;
long long a[N], b[N];

int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
        scanf("%lld", &a[i]);
    for (int i = 1; i <= n; i++)
        scanf("%lld", &b[i]);
    sort(a + 1, a + n + 1);
    sort(b + 1, b + n + 1);
    long long ans = 0;
    for (int i = 1; i <= n; i++)
        if (a[i] <= b[n - i + 1])
            ans += b[n - i + 1] - a[i];
    printf("%lld\n", ans);
    return 0;
}

1065 NC25025 [USACO 2007 Nov G]Sunscreen

题目描述

To avoid unsightly burns while tanning, each of the \(C\) (\(1 ≤ C ≤ 2500\)) cows must cover her hide with sunscreen when they're at the beach. Cow \(i\) has a minimum and maximum SPF rating (\(1 ≤ minSPF_i ≤ 1,000\); \(minSPF_i ≤ maxSPF_i ≤ 1,000\)) that will work. If the SPF rating is too low, the cow suffers sunburn; if the SPF rating is too high, the cow doesn't tan at all........
The cows have a picnic basket with \(L\) (\(1 ≤ L ≤ 2500\)) bottles of sunscreen lotion, each bottle \(i\) with an SPF rating \(SPF_i\) (\(1 ≤ SPF_i ≤ 1,000\)). Lotion bottle \(i\) can cover \(coveri\) cows with lotion. A cow may lotion from only one bottle.
What is the maximum number of cows that can protect themselves while tanning given the available lotions?

输入描述
  • Line 1: Two space-separated integers: \(C\) and \(L\)
  • Lines 2..C+1: Line \(i\) describes cow i's lotion requires with two integers: \(minSPF_i\) and \(maxSPF_i\)
  • Lines C+2..C+L+1: Line i+C+1 describes a sunscreen lotion bottle \(i\) with space-separated integers: \(SPF_i\) and \(coveri\)
输出描述

A single line with an integer that is the maximum number of cows that can be protected while tanning

示例

输入

3 2
3 10
2 5
1 5
6 2
4 1

输出

2
解题思路

区间覆盖。

  1. 奶牛排序逻辑:按右端点 \(r\)\(maxSPF\))从小到大排序。
  2. 防晒霜选择逻辑:在符合条件的防晒霜中,优先选择 \(SPF\) 值最小的。

证明:为什么按右端点排序?
假设当前有两头奶牛 \(C_1, C_2\),满足 \(r_1 < r_2\)
如果有一瓶防晒霜 \(S\),它既能满足 \(C_1\) 也能满足 \(C_2\)

  • 如果我们把 \(S\)\(C_2\) 用,那么 \(C_1\) 只能去寻找其他的防晒霜。但由于 \(C_1\) 的右端点 \(r_1\) 更小,它的选择范围严格小于或等于 \(C_2\)
  • 如果我们把 \(S\)\(C_1\) 用,\(C_2\) 依然可以去选择那些 \(SPF > r_1\)\(SPF \le r_2\) 的防晒霜。

结论:优先满足右端点小的奶牛,能为右端点大的奶牛腾出更大的选择空间。


证明:为什么选 \(SPF\) 最小的防晒霜?
假设对于奶牛 \(C_i\),有两瓶可用的防晒霜 \(S_{small}\)\(S_{large}\)(即 \(l_i \le spf_{small} < spf_{large} \le r_i\))。

  • 如果我们消耗了 \(S_{large}\),剩下 \(S_{small}\)。对于后续的奶牛 \(C_{i+1}\),由于我们已经按 \(r\) 排序,已知 \(r_{i+1} \ge r_i\)
  • 但是,\(C_{i+1}\) 的左端点 \(l_{i+1}\) 是不确定的。如果 \(l_{i+1} > spf_{small}\),那么 \(S_{small}\)\(C_{i+1}\) 来说就是废物
  • 在这种情况下,如果我们刚才用了 \(S_{small}\) 而留下了 \(S_{large}\)\(C_{i+1}\) 极有可能还能用上这瓶 \(S_{large}\)

结论:在满足当前约束的前提下,尽量使用“更小”的资源,可以为后面可能出现的“高门槛”需求(左端点更大的奶牛)保留更灵活的资源。

C++ 代码
#include <bits/stdc++.h>
using namespace std;
const int N = 2507;

int C, L;
struct Cow {
    int l, r;
} cow[N];

struct Lotion {
    int spf, cnt;
} lotion[N];

bool cmpCow(Cow a, Cow b) {
    if (a.r == b.r)
        return a.l < b.l;
    return a.r < b.r;
}

bool cmpLot(Lotion a, Lotion b) {
    return a.spf < b.spf;
}

int main() {
    cin >> C >> L;
    for (int i = 0; i < C; i++)
        cin >> cow[i].l >> cow[i].r;
    for (int i = 0; i < L; i++)
        cin >> lotion[i].spf >> lotion[i].cnt;

    sort(cow, cow + C, cmpCow);
    sort(lotion, lotion + L, cmpLot);

    int ans = 0;
    for (int i = 0; i < C; i++) {
        for (int j = 0; j < L; j++) {
            if (lotion[j].cnt &&
                lotion[j].spf >= cow[i].l &&
                lotion[j].spf <= cow[i].r) {
                ans++;
                lotion[j].cnt--;
                break;
            }
        }
    }
    cout << ans << "\n";
    return 0;
}
posted on 2026-03-02 09:48  Cocoicobird  阅读(2)  评论(0)    收藏  举报