Skip to content

贪心


一、思想🧀

Tip

把一个问题分成若干个小问题,找局部最优解


例题🧀

e.g.32【深基12.例1】部分背包问题🧀

题目描述

阿里巴巴走进了装满宝藏的藏宝洞。藏宝洞里面有 \(N(N \le 100)\) 堆金币,第 \(i\) 堆金币的总重量和总价值分别是 \(m_i,v_i(1\le m_i,v_i \le 100)\)。阿里巴巴有一个承重量为 \(T(T \le 1000)\) 的背包,但并不一定有办法将全部的金币都装进去。他想装走尽可能多价值的金币。所有金币都可以随意分割,分割完的金币重量价值比(也就是单位价格)不变。请问阿里巴巴最多可以拿走多少价值的金币?

输入格式

第一行两个整数 \(N,T\)

接下来 \(N\) 行,每行两个整数 \(m_i,v_i\)

输出格式

一个实数表示答案,输出两位小数

样例

样例输入

1
2
3
4
5
4 50
10 60
20 100
30 120
15 45

样例输出

240.00
#include <bits/stdc++.h>
using namespace std;
struct node {
    int weight;
    int value;
    double avg;
} e[100005];

bool cmp(node a, node b) {
    return a.avg > b.avg;
}

int main() {
    int n, t;
    cin >> n >> t;
    for (int i = 1; i <= n; i++) {
        cin >> e[i].weight >> e[i].value;
        e[i].avg = (double)e[i].value / e[i].weight;
    }
    sort(e + 1, e + n + 1, cmp);
    double ans = 0;
    for (int i = 1; i <= n; i++) {
        if (t >= e[i].weight) {
            ans += e[i].value;
            t -= e[i].weight;
        } else {
            ans += e[i].avg * t;
            t = 0;
            break;
        }
    }
    printf("%.2lf\n", ans);
}

e.g.33 凌乱的yyy / 线段覆盖🧀

题目背景

快 noip 了,yyy 很紧张!

题目描述

现在各大 oj 上有 \(n\) 个比赛,每个比赛的开始、结束的时间点是知道的。

yyy 认为,参加越多的比赛,noip 就能考的越好(假的)。

所以,他想知道他最多能参加几个比赛。

由于 yyy 是蒟蒻,如果要参加一个比赛必须善始善终,而且不能同时参加 \(2\) 个及以上的比赛。

输入格式

第一行是一个整数 \(n\),接下来 \(n\) 行每行是 \(2\) 个整数 \(a_{i},b_{i}\ (a_{i}<b_{i})\),表示比赛开始、结束的时间。

输出格式

一个整数最多参加的比赛数目。

样例

样例输入

1
2
3
4
3
0 2
2 4
1 3

样例输出

2

提示

  • 对于 \(20\%\) 的数据,\(n \le 10\)
  • 对于 \(50\%\) 的数据,\(n \le 10^3\)
  • 对于 \(70\%\) 的数据,\(n \le 10^{5}\)
  • 对于 \(100\%\) 的数据,\(1\le n \le 10^{6}\)\(0 \le a_{i} < b_{i} \le 10^6\)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6;
struct node
{
    ll l,r;
}e[N];
ll n;
bool cmp(node n1,node n2)
{
    return n1.r<n2.r;
}
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++) cin>>e[i].l>>e[i].r;
    sort(e+1,e+1+n,cmp);
    int ans=0,t=0;
    for(int i=1;i<=n;i++)
    {
        if(t<=e[i].l)
        ans++,t=e[i].r;
    }
    cout<<ans<<endl;
    return 0;
}

e.g.34 [USACO05NOV] 奶牛玩杂技🧀

题目背景

Farmer John 养了 \(N\) 头牛,她们已经按 \(1\sim N\) 依次编上了号。FJ 所不知道的是,他的所有牛都梦想着从农场逃走,去参加马戏团的演出。可奶牛们很快发现她们那笨拙的蹄子根本无法在钢丝或晃动的的秋千上站稳(她们还尝试过把自己装在大炮里发射出去,但可想而知,结果是悲惨的) 。最终,她们决定练习一种最简单的杂技:把所有牛都摞在一起, 比如说, 第一头牛站在第二头的身上, 同时第二头牛又站在第三头牛的身上...最底下的是第 \(N\) 头牛。

题目描述

每头牛都有自己的体重以及力量,编号为 \(i\) 的奶牛的体重为 \(W_i\),力量为 \(S_i\)

当某头牛身上站着另一些牛时它就会在一定程度上被压扁,我们不妨把它被压扁的程度叫做它的压扁指数。对于任意的牛,她的压扁指数等于摞在她上面的所有奶牛的总重(当然不包括她自己)减去它的力量。奶牛们按照一定的顺序摞在一起后, 她们的总压扁指数就是被压得最扁的那头奶牛的压扁指数。

你的任务就是帮助奶牛们找出一个摞在一起的顺序,使得总压扁指数最小。

输入格式

第一行一个整数 \(N\)

接下来 \(N\) 行,每行两个整数 \(W_i\)\(S_i\)

输出格式

一行一个整数表示最小总压扁指数。

样例

样例输入

1
2
3
4
3
10 3
2 5
3 3

样例输出

2

提示

对于 \(100\%\) 的数据,\(1 \le N \le 5\times 10^4\)\(1 \le W_i \le 10^4\),$


e.g. CF2124B Minimise Sum🧀

题目描述

本题与 G 题不同,在本题中您必须在最多一次的操作后输出前缀最小值的最小和。

给定一个整数 \(n\) 与一个长度为 \(n\) 的数组 \(a(0\leq a_i \leq n)\),可以执行以下操作:

  • 选择两个整数 \(i,j(i<j)\)。令 \(a_i = a_i + a_j\),同时令 \(a_j=0\)

需要注意的是,上面的操作只能执行最多一次。

输出可能的 \(\min(a_1)+\min(a_1,a_2)+\ldots+\min(a_1,a_2,\ldots,a_n)\) 的最小值。

输入格式

每个测试包含多个测试用例。

测试数据的第一行为一个整数 \(t(1\leq t\leq 10^4)\),表示测试用例的组数。

每组测试用例共两行,第一行为一个整数 \(n(2\leq n\leq2\times10^5)\)

第二行为 \(n\) 个整数,表示题目中的数组 \(a\)

测试数据保证所有 \(n\) 的和不超过 \(2\times10^5\)

输出格式

对于每一组测试用例,输出可能的 \(\min(a_1)+\min(a_1,a_2)+\ldots+\min(a_1,a_2,\ldots,a_n)\) 的最小值。

输入输出样例 #1

输入 #1

1
2
3
4
5
6
7
3
2
1 2
3
1 2 3
4
3 0 2 3

输出 #1

1
2
3
2
2
3

说明/提示

在第二个测试用例中,在 \(i=2,j=3\) 时操作可以得到最优解。

在第三个测试用例中,不操作即为最优解。

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define vec vector
int32_t main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);

    int t;
    cin >> t;
    vec<int> a(2e5);
    for (int i = 0; i < t; i++) {
        int n;
        cin >> n;
        for (int j = 0; j < n; j++) {
            cin >> a[j];
        }
        // 判断第一第二个即可,第一个小于第二个就后面全是零所以两倍第一个,否则第一个加第二个
        if (a[0] <= a[1]) {
            cout << 2 * a[0] << endl;
        } else {
            cout << a[0] + a[1] << endl;
        }
    }
    return 0;
}