洛谷题单——【算法1-5】贪心

vAHwSP.jpg
但行好事,莫问前程。



题单名称

【算法1-5】贪心

P2240 【深基12.例1】部分背包问题

题目描述

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

输入格式

第一行两个整数 N , T N,T N,T

接下来 N N N 行,每行两个整数 m i , v i m_i,v_i mi,vi

输出格式

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

样例 #1

样例输入 #1

4 50
10 60
20 100
30 120
15 45

样例输出 #1

240.00

思路

题目说是背包问题,其实和背包问题关系不大

做法就是贪心

对于每一堆金币创建一个结构体,内部存放着金币的个数和重量

对于金币重量比对结构体数组进行排序

然后从前往后尽可能多的拿取金币即可

代码

// P2240 【深基12.例1】部分背包问题
// 贪心+结构体排序
#include <bits/stdc++.h>
using namespace std;
struct gold
{
    int w;
    float v;
}gg[110];

bool cmp(gold a, gold b){
    //回调函数
    return (a.v/a.w) > (b.v/b.w);
}

int main(int argc, char const *argv[])
{
    float ret = 0;
    float nowt,t;
    int n;
    cin >> n >> t;
    nowt = t;
    //读取数据
    for(int i=0; i<n; i++){
        cin >> gg[i].w >> gg[i].v;
    }
    sort(gg, gg+n, cmp);
    for(int i=0; i<n; i++){
        if(gg[i].w <= nowt){
            ret += gg[i].v;
            nowt -= gg[i].w;
        } else if(gg[i].w > nowt){
            ret += nowt*gg[i].v/gg[i].w;
            break;
        }
    }

    printf("%.2f",ret);
    return 0;
}

P1223 排队接水

题目描述

n n n 个人在一个水龙头前排队接水,假如每个人接水的时间为 T i T_i Ti,请编程找出这 n n n 个人排队的一种顺序,使得 n n n 个人的平均等待时间最小。

输入格式

第一行为一个整数 n n n

第二行 n n n 个整数,第 i i i 个整数 T i T_i Ti 表示第 i i i 个人的等待时间 T i T_i Ti

输出格式

输出文件有两行,第一行为一种平均时间最短的排队顺序;第二行为这种排列方案下的平均等待时间(输出结果精确到小数点后两位)。

样例 #1

样例输入 #1

10 
56 12 1 99 1000 234 33 55 99 812

样例输出 #1

3 2 7 8 1 4 9 6 10 5
291.90

提示

n ≤ 1000 , t i ≤ 1 0 6 n \leq 1000,t_i \leq 10^6 n1000,ti106,不保证 t i t_i ti 不重复。

t i t_i ti 重复时,按照输入顺序即可(sort 是可以的)

思路

还是结构体+排序,主要是要储存每个人的序号和打水时间以及等待的时间

然后对于打水时间进行排序,打水打的快的人先打

这样会让等待的人数迅速减少,进而减少人群的平均等待时间

代码

// P1223 排队接水
// 贪心,结构体排序
#include <bits/stdc++.h>
using namespace std;

struct p{
    int uid;
    int t;
    int wait;
}pp[1010];

bool cmp(p a, p b){
    return a.t < b.t; 
}

int main(){
    int n;
    double avewait = 0;//坑爹啊,这题要用双精度
    cin >> n;

    // 读取数据
    for(int i=0; i<n; i++){
        pp[i].uid = i+1;
        cin >> pp[i].t;
        pp[i].wait = 0;
    }
    // 对数据依据打水时间排序
    sort(pp, pp+n, cmp);
    // 计算等待时间
    for(int i=0; i<n; i++){
        for(int j=0; j<i; j++){
            pp[i].wait += pp[j].t;
        }
        avewait += pp[i].wait;
    }
    // 计算平均等待时间
    avewait = avewait*1.0/n*1.0;
    for(int i=0; i<n-1; i++){
        cout << pp[i].uid << " ";
    }
    cout << pp[n-1].uid << endl;
    printf("%.2f",avewait);
}

P1803 凌乱的yyy / 线段覆盖

题目背景

快 noip 了,yyy 很紧张!

题目描述

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

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

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

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

输入格式

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

输出格式

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

样例 #1

样例输入 #1

3
0 2
2 4
1 3

样例输出 #1

2

提示

对于 20 % 20\% 20% 的数据, n ≤ 10 n \le 10 n10

对于 50 % 50\% 50% 的数据, n ≤ 1 0 3 n \le 10^3 n103

对于 70 % 70\% 70% 的数据, n ≤ 1 0 5 n \le 10^{5} n105

对于 100 % 100\% 100% 的数据, 1 ≤ n ≤ 1 0 6 1\le n \le 10^{6} 1n106 0 ≤ a i < b i ≤ 1 0 6 0 \le a_{i} < b_{i} \le 10^6 0ai<bi106

思路

用结构体保存所有线段的起始位置和结束位置

然后以右端点的位置排序所有线段

从前往后不断的排线段就可以了

代码

// P1803 凌乱的yyy / 线段覆盖

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

struct line{
    int left;
    int right;
    int use;
}l[1000010];

int cnt = 0;
int n;

void find(int head){
    for(int i=0; i<n; i++){
        if(l[i].left >= head && l[i].use == 0){
            l[i].use = 1;
            cnt++;
            find(l[i].right);
            break;//TLE后加的代码,找到的递归情况一定是最佳,无序再找,直接跳出
        }
    }
    return;
}

bool cmp(line a, line b){
    return a.right < b.right;
}

int main(int argc, char const *argv[])
{
    cin >> n;
    for(int i=0; i<n; i++){
        cin >> l[i].left >> l[i].right;
        l[i].use = 0;
    }
    sort(l, l+n, cmp);
    find(0);
    cout << cnt;
    return 0;
}

P1090 [NOIP2004 提高组] 合并果子 / [USACO06NOV] Fence Repair G

题目描述

在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆。多多决定把所有的果子合成一堆。

每一次合并,多多可以把两堆果子合并到一起,消耗的体力等于两堆果子的重量之和。可以看出,所有的果子经过 n − 1 n-1 n1 次合并之后, 就只剩下一堆了。多多在合并果子时总共消耗的体力等于每次合并所耗体力之和。

因为还要花大力气把这些果子搬回家,所以多多在合并果子时要尽可能地节省体力。假定每个果子重量都为 1 1 1 ,并且已知果子的种类 数和每种果子的数目,你的任务是设计出合并的次序方案,使多多耗费的体力最少,并输出这个最小的体力耗费值。

例如有 3 3 3 种果子,数目依次为 1 1 1 2 2 2 9 9 9 。可以先将 1 1 1 2 2 2 堆合并,新堆数目为 3 3 3 ,耗费体力为 3 3 3 。接着,将新堆与原先的第三堆合并,又得到新的堆,数目为 12 12 12 ,耗费体力为 12 12 12 。所以多多总共耗费体力 = 3 + 12 = 15 =3+12=15 =3+12=15 。可以证明 15 15 15 为最小的体力耗费值。

输入格式

共两行。
第一行是一个整数 n ( 1 ≤ n ≤ 10000 ) n(1\leq n\leq 10000) n(1n10000) ,表示果子的种类数。

第二行包含 n n n 个整数,用空格分隔,第 i i i 个整数 a i ( 1 ≤ a i ≤ 20000 ) a_i(1\leq a_i\leq 20000) ai(1ai20000) 是第 i i i 种果子的数目。

输出格式

一个整数,也就是最小的体力耗费值。输入数据保证这个值小于 2 31 2^{31} 231

样例 #1

样例输入 #1

3 
1 2 9

样例输出 #1

15

提示

对于 30 % 30\% 30% 的数据,保证有 n ≤ 1000 n \le 1000 n1000

对于 50 % 50\% 50% 的数据,保证有 n ≤ 5000 n \le 5000 n5000

对于全部的数据,保证有 n ≤ 10000 n \le 10000 n10000

思路

这题的思路很简单,对于排序的数组取最小的两个加和,然后再从加和后的数组中寻找两个最小值继续加和,反复如此。

问题是时间复杂度的限制,原本我是循环加和n-1次,在这个过程中每次都进行依次sort排序,然后超时了

然后采用了STL库的优先队列,二者都基于堆排序,区别在于,sort函数是对一整个数组进行重新排序,而用优先队列就可以只取数2个,加和后再入队一个数,减少了重构的代价。

代码

// P1090 [NOIP2004 提高组] 合并果子 / [USACO06NOV] Fence Repair G
// O(n2logn)
#include <bits/stdc++.h>
using namespace std;
priority_queue<int,vector<int>,greater<int>> a;

int main(){
    int n;
    cin >> n;
    for(int i=0; i<n; i++){
        int temp;
        cin >> temp;
        a.push(temp);
    }
    long long weight = 0;
    for(int i=0; i<n-1; i++){
        int ta = a.top();
        a.pop();
        int tb = a.top();
        a.pop();
        weight += ta + tb;
        a.push(ta + tb);
    }
    cout << weight;
    return 0;
}

P3817 小A的糖果

题目描述

小 A 有 n n n 个糖果盒,第 i i i 个盒中有 a i a_i ai 颗糖果。

小 A 每次可以从其中一盒糖果中吃掉一颗,他想知道,要让任意两个相邻的盒子中糖的个数之和都不大于 x x x,至少得吃掉几颗糖。

输入格式

输入的第一行是两个用空格隔开的整数,代表糖果盒的个数 n n n 和给定的参数 x x x

第二行有 n n n 个用空格隔开的整数,第 i i i 个整数代表第 i i i 盒糖的糖果个数 a i a_i ai

输出格式

输出一行一个整数,代表最少要吃掉的糖果的数量。

样例 #1

样例输入 #1

3 3
2 2 2

样例输出 #1

1

样例 #2

样例输入 #2

6 1
1 6 1 2 0 4

样例输出 #2

11

样例 #3

样例输入 #3

5 9
3 1 4 1 5

样例输出 #3

0

提示

样例输入输出 1 解释

吃掉第 2 盒中的一个糖果即可。


样例输入输出 2 解释

第 2 盒糖吃掉 6 6 6 颗,第 4 盒吃掉 2 2 2 颗,第 6 盒吃掉 3 3 3 颗。


数据规模与约定
  • 对于 30 % 30\% 30% 的数据,保证 n ≤ 20 n \leq 20 n20 a i , x ≤ 100 a_i, x \leq 100 ai,x100
  • 对于 70 % 70\% 70% 的数据,保证 n ≤ 1 0 3 n \leq 10^3 n103 a i , x ≤ 1 0 5 a_i, x \leq 10^5 ai,x105
  • 对于 100 % 100\% 100% 的数据,保证 2 ≤ n ≤ 1 0 5 2 \leq n \leq 10^5 2n105 0 ≤ a i , x ≤ 1 0 9 0 \leq a_i, x \leq 10^9 0ai,x109

思路

两个盒子的和数超过了限制

那么这里如果从前往后减,则优先减去后面盒子的数,如果后面盒子不够减,再减前面盒子的数

因为这样可以尽可能的让减的盒子处于中央位置,减去的糖果数可以影响到前后两个盒子

代码

// P3817 小A的糖果
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int main(int argc, char const *argv[])
{
    ll ret = 0;
    int n;
    int x;
    vector<int> a;
    cin >> n >> x;
    //读取数据
    for(int i=0; i<n; i++){
        int temp;
        cin >> temp;
        a.push_back(temp);
    }
    for(int i=1; i<n; i++){
        if(a[i]+a[i-1] > x){
            int dec = a[i] + a[i-1] - x;
            //两个盒子的糖果数和超出x
            if(a[i] >= dec){
                a[i] -= dec;
                ret += dec;
            } else if(a[i] < dec){
                ret += dec;
                a[i-1] -= dec - a[i];
                a[i] = 0;
            }    
        }
    }
    cout << ret;
    return 0;
}

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

introversi0n

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值