板刷贪心总结

题单

AT_agc029_a [AGC029A] Irreversible operation(橙)

为什么我自己没有做出来

其实这道题就是求逆序对的个数

那就从后往前遍历,统计遇到的 W 个数,遇到 B 就将答案加上 W 的个数

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
//不开long long 见祖宗
int n, w, ans;
string s;
signed main()
{
    cin >> s;
    s = " " + s;
    n = s.size() - 1;
    for(int i = n; i >= 1; i --)
    {
        if(s[i] == 'W')
        {
            w ++;
        }
        if(s[i] == 'B')
        {
            ans += w;
        }
    }
    cout << ans << endl;
}

P2949 [USACO09OPEN] Work Scheduling G(蓝)

反悔贪心

按照人类逻辑,给截止时间从小到大排个序,优先做紧急的事情,在紧急的事情中优先完成报酬高的事情

但是万一后面的事情全都是报酬高的人类逻辑就不是最优的了

于是我们开始反悔,不做之前做过的事情中报酬最低的那个以完成报酬高的任务,可以用一个小根堆维护报酬

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n;
struct node
{
    int val, t;
}a[100005];
priority_queue<int, vector<int>, greater<int> > q;
bool cmp(node x, node y)
{
    return x.t < y.t;
}
signed main()
{
    cin >> n;
    for(int i = 1; i <= n; i ++)
    {
        cin >> a[i].t >> a[i].val;
    }
    sort(a + 1, a + n + 1, cmp);
    for(int i = 1; i <= n; i ++)
    {
        if(q.size() < a[i].t)
        {
            q.push(a[i].val);
        }
        else if(q.top() < a[i].val)
        {
            q.pop();
            q.push(a[i].val);
        }
    }
    int ans = 0;
    while(!q.empty())
    {
        ans += q.top();
        q.pop();
    }
    cout << ans << endl;
}

P2209 [USACO13OPEN] Fuel Economy S(蓝)

先给所有加油站到起点的距离从小到大排个序

先考虑不能到达的情况

  • 第一个加油站到起点的距离 > 初始油量
  • 途中存在两个加油站之间的距离 > 油箱容量
  • 最后一个加油站到终点的距离 > 油箱容量

排除不可能到达的情况后再进行贪心

  1. 加满足够的油,刚好跑到能跑到的第一个比现在这个加油站便宜的加油站

  2. 如果在加满油能跑到的范围内没有比当前加油站便宜的加油站,就在当前加油站加满油跑到能跑到的范围内最便宜的加油站

需要注意的一点是不能在判断 1 中跑到当前最便宜的加油站,而是看到一个便宜就跑过去 (我在这里卡了好久

然后就做完这道题了QAQ

P2431 正妹吃月饼(绿)

\(b\) 进行二进制拆分以后,对于每个位的1,我们可以把它去掉,然后直接把比它低的位全置1(将大的月饼分割成之前的小月饼)。

从高到低枚举1,当第一次产生的新数比A大时,就可以得出答案

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
int a, b, _, len, ans;
int cal(int x)
{
    int cnt = 0;
    while (x)
    {
        x -= x & -x;
        cnt++;
    }
    return cnt;
}
signed main()
{
    cin >> a >> b;
    _ = b;
    while (_)
    {
        len++;
        _ >>= 1;
    }
    ans = max(cal(b), ans);
    int qwq = 1;
    for (int i = len; i; i--)
    {
        if ((b >> i - 1) & 1)
        {
            int t = b ^ (qwq << i - 1);
            t |= (qwq << i - 1) - 1;
            if (t >= a)
            {
                ans = max(cal(t), ans);
            }
                
        }
    }
    cout << ans << endl;
}

[P3045 [USACO12FEB] Cow Coupons G]([P3045 USACO12FEB] Cow Coupons G - 洛谷)

题意

FJ 准备买一些新奶牛。市场上有 \(N\) 头奶牛,第 \(i\) 头奶牛价格为 \(P_i\)。FJ 有 \(K\) 张优惠券,使用优惠券购买第 \(i\) 头奶牛时价格会降为 \(C_i\),当然每头奶牛只能使用一次优惠券。FJ 想知道花不超过 \(M\) 的钱最多可以买多少奶牛?

  • \(1 \le K \le N \le 5 \times 10^4\)

思路

我们考虑先买下所有 \(C\) 最小的奶牛。

接着考虑反悔的条件,记用优惠券买的奶牛为 \(j\) ,当前奶牛为 \(i\)。如果用优惠券买当前奶牛加上用原价买 \(j\) 小于用优惠券买 \(j\) 加上原价买 \(i\),就可以反悔。于是可以得到反悔的如下等式:$$C_j + P_i > P_j + C_i$$

移向变为:\(P_j - C_j < P_i - C_i\)

所以我们把奶牛按照 \(P_i - C_i\) 排序就可以正常反悔贪心了

代码

#include<bits/stdc++.h>
using namespace std;
#define int long long

const int N = 5e4 + 5;
struct COW
{
    int p, c;
}a[N];
struct node
{
    int data, id;
    // bool operator<(const node it)const
    // {
    //     return data < it.data;
    // }
    bool operator>(const node it)const
    {
        return data > it.data;
    }
};
priority_queue<node, vector<node>, greater<node> > q1, q2;
priority_queue<int, vector<int>, greater<int> > q3;

int n, k, m, sum, ans, vis[N];

signed main()
{
    cin >> n >> k >> m;
    for(int i = 1; i <= n; i ++)
    {
        cin >> a[i].p >> a[i].c;
        q1.push({a[i].p, i});
        q2.push({a[i].c, i});
    }
    for(int i = 1; i <= k; i ++)
    {
        q3.push(0);
    }
    while(!q1.empty())
    {
        node x1 = q1.top();
        node x2 = q2.top();
        if(vis[x1.id])
        {
            q1.pop();
            continue;
        }
        if(vis[x2.id])
        {
            q2.pop();
            continue;
        }
        if(x1.data < x2.data + q3.top())
        {
            if(sum + x1.data > m)
            {
                break;
            }
            vis[x1.id] = 1;
            q1.pop();
            sum += x1.data;
            ans ++;
        }
        else
        {
            if(sum + x2.data + q3.top() > m)
            {
                break;
            }
            sum += x2.data + q3.top();
            ans ++;
            q2.pop();
            vis[x2.id] = 1;
            q3.pop();
            q3.push(a[x2.id].p - a[x2.id].c);
        }
    }
    cout << ans << endl;
}

[P2107 小 Z 的 AK 计划](P2107 小 Z 的 AK 计划 - 洛谷)

题意

机房一条街有 \(n\) 个机房,第 \(i\) 个机房的坐标为 \(x_i\),从 \(x_1\)\(x_2\) 所耗费的时间为 \(|x_1 - x_2|\),初始在 \(0\) 位置。小 Z 到达第 \(i\) 个机房后,可以花 \(t_i\) 的时间AK。当然,也可以过机房而不入。

小 Z 现在只有 \(m\) 个单位时间,现在他想知道自己最多能在多少个机房 AK。

\(1 \leq n \leq 10^5\)\(0 \leq m,x_i \leq 10^{18}\)\(0 \leq t_i \leq 10^9\)

思路

考虑反悔贪心。每到达一个机房就AK,如果时间超过了就把需要时间最大的机房给取消AK,返还时间,直到时间不超过为止。

注意事项

  • 初始位置是 \(0\)
  • 要按 \(x_i\) 排序

代码

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n, m, ans, now, lst, cnt;
struct node
{
    int x, t;
}a[100005];
priority_queue<int, vector<int>, greater<int> > q;
signed main()
{
    cin >> n >> m;
    for (int i = 1; i <= n; i++)
        cin >> a[i].x >> a[i].t;
    sort(a + 1, a + n + 1, [](node a, node b) { return a.x < b.x; });
    for(int i = 1; i <= n; i ++)
    {
        now += a[i].x - lst;
        lst = a[i].x;
        q.push(a[i].t);
        ans ++;
        now += a[i].t;
        while(!q.empty() && now > m)
        {
            ans --;
            now -= q.top();
            q.pop();
        }
        if(now > m)
            break;
        cnt = max(cnt, ans);
    }
    cout << cnt << endl;
}
posted @ 2026-02-13 19:40  _Flins  阅读(3)  评论(0)    收藏  举报