20250724

贪心世界第一好玩!

CF1601D Difficult Mountain

神秘贪心

思路

发现当 \(a < d\) 时没有任何影响,所以将所有 \(a < d\) 的登山者全部放到前面。

然后以 \(\max(a_{i},s_{i})\) 为第一关键字, \(s_{i}\) 为第二关键字从小到大排序。

然后依次枚举,能选就选,就是最优答案。

证明:

\(\max(a,s)=a/s\) 分类讨论。

  • \(\max_{i}=a_{i},\max_{j}=a_{j}\) ,不妨设 \(a_{i}<a_{j}\) ,如果 \(j\) 先上, \(i\) 一定上不去,所以不如让 \(i\) 先去试试。

  • \(\max_{i}=a_{i},\max_{j}=s_{j}\) ,如果 \(a_{i}>s_{j}\) 则同上, \(i\) 先上, \(j\) 一定上不去,所以不如让 \(j\) 先去试试。 如果 \(a_{i}\le s_{j}\) ,那么 \(i\) 先上对 \(j\) 显然不会有影响。

  • \(\max_{i}=s_{i},\max_{j}=s_{j}\) ,不妨设 \(s_{i}<s_{j}\) ,如果 \(s_{i}<a_{j}\) ,显然是要 \(i\) 先去试试,如果 \(s_{i}>a_{j}\) ,先让 \(i\) 上,对 \(j\) 显然没有影响。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 5e5 + 5;
int ans, d, nn, n;
struct node
{
    int s, a;
}a[N];
bool cmp(node a, node b)
{
    if(max(a.a, a.s) == max(b.a, b.s))
    {
        return a.s < b.s;
    }
    return max(a.a, a.s) < max(b.a, b.s);
}
signed main()
{
    cin >> n >> d;
    for(int i = 1; i <= n; i ++) cin >> a[i].s >> a[i].a;
    sort(a + 1, a + n + 1, cmp);
    for(int i = 1; i <= n; i ++)
    {
        if(a[i].s >= d)
        {
            ans ++;
            d = max(d, a[i].a);
            // cout << "qwq: "<< a[i].s << " " << a[i].a << endl;
        }
    }
    cout << ans << endl;
}

AT_arc121_d [ARC121D] 1 or 2

思路

当只能吃两颗糖时,用最小的匹配最大的、次小的匹配次大的、第三小的匹配第三大的……一定是最优的。

原理小学生都会,这里就不证明了。

那么考虑只能吃一颗的情况。

考虑在糖果中加入美味度为 \(0\) 的糖果,那么就可以像只吃两颗糖一样匹配了。

代码要点:

  • 美味度为 \(0\) 的糖果需要加入序列中并重新排序(因为会有负数的糖果qwq)。
  • 可能糖果总数不是偶数,那么会出现没有匹配上的糖果,需要单独处理。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int inf = 1e18;
const int N = 5e5 + 5;
int n, a[N], minn, maxx, ans = inf;
signed main()
{
    cin >> n;
    for(int i = 1; i <= n; i ++) cin >> a[i];
    for(int i = n % 2; i <= n; i += 2)
    {
        sort(a + 1, a + n + 1);
        minn = inf, maxx = -inf;
        int l = 1, r = n;
        while(l < r)
        {
            // cout <<"qwq: "<< l << " " << r << " " << a[l]<< " " << a[r] << endl;
            minn = min(minn, a[l] + a[r]);
            maxx = max(maxx, a[l] + a[r]);
            // cout << minn << " " << maxx << endl;
            l ++, r --;
        }
        if(l == r)
        {
            minn = min(minn, a[r]);
            maxx = max(maxx, a[r]);
        }
        ans = min(ans, maxx - minn);
        n ++;
        a[n]= 0;
    }
    cout << ans << endl;
}

P8162 [JOI 2022 Final] 让我们赢得选举 / Let's Win the Election

思路

显然,对于每个州,演讲时间只有 \(0\)\(a_{i}\)\(b_{i}\) 三种情况。

定义演讲时间为 \(a_{i}\) 的州为 \(\operatorname{A}\) 类州(即只获得选票),演讲时间为 \(b_{i}\) 的州为 \(\operatorname{B}\) 类州(即获得协作者),演讲时间为 \(0\) 的州为 \(\operatorname{C}\) 类州(即不去演讲)。

先去演讲 \(\operatorname{B}\) 类州,再去演讲 \(\operatorname{A}\) 类州是更优的。

所以我们按照 \(b_{i}\) 排序。

我们枚举 \(b_{i}\) ,进行 \(\operatorname{dp}\)

由于是按照 \(b_{i}\) 升序排列的,那么如果 \(\operatorname{B}\) 类州之前存在 \(\operatorname{C}\) 类州,让该C类州成为 \(\operatorname{B}\) 类州,该 \(\operatorname{B}\) 类州成为 \(\operatorname{C}\) 类州是更优的。

那么序列的前半部分只有 \(\operatorname{A}\) 类州或 \(\operatorname{B}\) 类州,后半部分只有 \(\operatorname{A}\) 类州或 \(\operatorname{C}\) 类州。

所以定义 \(dp_{i,j}\) 表示枚举到第 \(i\) 个州,选了 \(j\)\(\operatorname{B}\) 类州的最少时间。

枚举 \(\operatorname{B}\) 类州的个数 \(\operatorname{sum}\),然后进行 \(\operatorname{dp}\)

\(\operatorname{dp}\)\(\operatorname{sum}\) 后停止,计算剩下的州中最小的 \(\operatorname{A}\) 类州的时间。

设后 \(x\) 个州里,\(a_{i}\) 最小的 \(y\) 个州的 \(a_{i}\) 之和为 \(w(x,y)\)

那么答案就是 \(\min_{i = sum}^{k}dp_{i,sum} + w(n - i,k - i)\)

\(\operatorname{dp}\) 的时间复杂度是 \(O(n^{2})\),由于要枚举 \(\operatorname{B}\) 类州的个数,所以总时间复杂度 \(O(n^{3})\)

点击查看代码
#include<bits/stdc++.h>
#define ldb double
using namespace std;
const int inf = 1e9;
const int N = 505;

struct node
{
    int a, b;
}a[N], aa[N];
bool cmp(node a, node b)
{
    return a.b < b.b;
}
bool cmp1(node a, node b)
{
    return a.a < b.a;
}

int n, k;
ldb dp[N][N], f[N][N], ans = inf;

signed main()
{
    cin >> n >> k;
    for(int i = 1; i <= n; i ++)
    {
        cin >> a[i].a >> a[i].b;
        if(a[i].b == -1) a[i].b = inf;
        aa[i].a = a[i].a, aa[i].b = a[i].b;
    }
    sort(aa + 1, aa + n + 1, cmp);
    sort(a + 1, a + n + 1, cmp);
    for(int i = n; i >= 1; i --)//预处理
    {
        sort(aa + i, aa + n + 1, cmp1);
        for(int j = i; j <= n; j ++) f[i][j - i + 1] = f[i][j - i] + aa[j].a;
    }
    for(int i = 0; i < k; i ++)//枚举找几个人
    {
        for (int l = 0; l <= k; l++) for (int j = 0; j <= i; j++) dp[l][j] = inf;
        dp[0][0] = 0;
        for(int j = 1; j <= k; j ++)
        {
            dp[j][0] = dp[j - 1][0] + a[j].a * 1.0 / (i + 1);
            for(int l = 1; l <= i; l ++)
            {
                dp[j][l] = min(dp[j - 1][l - 1] + a[j].b * 1.0 / l, dp[j - 1][l] + a[j].a * 1.0 / (i + 1));
            }
        }
        for(int j = 0; j <= k; j ++)
        {
            ans = min(ans, dp[j][i] + f[j + 1][k - j] * 1.0 / (i + 1));
        }
    }
    printf("%.5lf", ans);
}
posted @ 2026-07-24 16:02  Luowj  阅读(3)  评论(0)    收藏  举报