贪心入门+10道例题+解析代码

 梦寻古镇 - 羽翼深蓝Wings - 单曲 - 网易云音乐

 如果我有一个男朋友 - 于娜懿 - 单曲 - 网易云音乐

 对酒(女生版) - 浅影阿 - 单曲 - 网易云音乐

 知我(抒情版) - 尘ah. - 单曲 - 网易云音乐

 骏马赞 (重新录制) - 九宝乐队 - 单曲 - 网易云音乐

终于要开始算法入门了,学完再刷点题,应该就是算法小白了吧

别人大一下都会哈夫曼,平衡二叉树,启发式这些东西了,好好加油,分配好注意力

最近还了解到字节青训营,加入首先要笔试,考算法+八股,那我就先攻克算法吧

(企业员工手把手教你做项目~差的有点远,先好好打基础)

不必妄自菲薄,很多学了几年,也只会黑框输入的大有人在,什么时候开始都不晚

目录

算法之美

算法的优劣

复杂度计算方法

贪心算法

贪心本质

最优装载问题

贪心刷题

1832: [NewOJ Week 9] 配对

1785: [NewOJ Contest 10] 剪绳子

P1589 泥泞路

P1208 [USACO1.3]混合牛奶 Mixing Milk

P4995 跳跳!

P1094 [NOIP2007 普及组] 纪念品分组

P1199 [NOIP2010 普及组] 三国游戏

P2672 [NOIP2015 普及组] 推销员

P1080 [NOIP2012 提高组] 国王游戏

总结


洛谷官方题单

一个动态更新的洛谷综合题单 - Studying Father's luogu blog - 洛谷博客

算法之美

算法的优劣

斐波那契数列举例

1,按照数列表达式,递归(7秒出结果)

#include
using namespace std;

long double fib1(int n)
{
    if(n < 1)
        return -1;
    else if(n == 1 || n == 2)
        return 1;
    else
        return fib1(n - 1) + fib1(n - 2);
}

int main()
{
    int n;
    cin>>n;
    cout<
45
1.1349e+009

2,数组存储每一项,从前往后递推,非递归(瞬间出结果)

#include
using namespace std;

long double fib2(int n)
{
    long double temp;
    if(n < 1) return -1;
    //new动态分配内存, 创建数组, 首地址赋值给指针变量a
    long double *a = new long double[n + 1];
    a[1] = 1, a[2] = 1;
    for(int i = 3; i <= n; ++i)
        a[i] = a[i - 1] + a[i - 2];
    temp = a[n];
    delete []a; //释放数组所占内存
    return temp;
}

int main()
{
    int n;
    cin>>n;
    cout<
45
1.1349e+009

总结

除了时间,空间复杂度,就是算法健壮性

何谓健壮性:算法对非法数据及操作有较好的反应和处理,eg: 信息管理系统中登记电话号码时,少数如1位,系统就应该提示出错 

补充:时间复杂度,并不是真的计算运行时间,而是将算法基本运算的执行次数,作为标准

复杂度计算方法

时间复杂度:高效率

举例1

int sum(int n) {
    int sum = 0; //1次
    for(int i = 0; i <= n; ++i) //n + 1次
        sum += i; //n次
    return sum; //1次
}

执行次数为2n + 3,T(n) = 2*n + 3,当n足够大,舍去小项和系数,所以时间复杂度为O(n)

举例2

sum = 0; //1次
total = 0; //1次
for(i = 1; i <= n; ++i) { //n + 1次, 最后一次判断条件不成立,结束
    sum = sum + i; //n次
    for(j = 1; j <= n; ++j) //n*(n + 1)次
        total = total + i*j; //n * n次
}

计算贡献最大的语句即可,也就是O(n^2)

类似排序,插入,查找等算法,不能直接计算运行次数,可以按最好,最坏,平均3种情况求算法渐进复杂度

当然,通常只看最坏复杂度,它对于衡量算法好坏,具有实际意义 

空间复杂度:低存储

空间复杂度 = 输入输出数据 + 算法本身 + 额外的辅助空间

其中,辅助空间是衡量空间复杂度的关键

举例1

swap(int x, int y) { //x与y交换
    int temp;
    temp = x; //temp为辅助空间
    x = y;
    y = temp;
}

上述空间复杂度为O(1)

举例2

递归中,每次递归都需要一个栈空间来保存调用记录,因此计算空间复杂度时

需要计算递归栈的辅助空间

以计算n的阶乘为例

long long fac(int n) {
    if(n < 0) return -1;
    else if(n == 0 || n == 1) return 1;
    else return n * fac(n - 1);
}

递推 + 回归在系统内部使用栈实现,栈空间的大小为递归树的深度,如下图

贪心入门+10道例题+解析代码_第1张图片计算n的阶乘时,递归树的深度为n,

所以n阶乘递归算法的空间复杂度为O(n)

具体分析 + 形象举例

常见算法时间复杂度:

1,常数阶:O(1)     

2,对数阶:O(logn), O(nlogn)

3,多项式阶:O(n), O(n^2), O(n^3)

4,指数阶:O(2^n), O(n!), O(n^n)

贪心入门+10道例题+解析代码_第2张图片

1 < logn < n < nlogn < n^2 < n^3 < 2^n < n! < n^n

关于复杂度的形象解释

一天 ≈ 10^5秒,一年 ≈ 3 * 10^7秒,100年 ≈ 3 * 10^9,三生三世 ≈ 10^10秒

假设有10亿数据排序,10亿 = 10^9

普通PC:10^9次运算每秒,

当复杂度O(n^2),(10^9)^2 = 10^18,需要10^18 / 10^9 = 10^9秒 ≈ 30年

当复杂度O(nlogn),需要10^9 * log10^9 / 10^9 ≈ 30秒(计算机一般用二进制,此处对数的底数也是二进制,所以log10 ≈ 3,3 * 9 ≈ 30)

超级计算机:10^17次 / 秒

复杂度O(n^2)需要10秒

复杂度O(nlogn)需要3 * 10^-7秒 

注意:2^10 = 1024 ≈ 10^3,2^30 ≈ 10^9,log10^3 ≈ log2^10 ≈ 10,log10^9 ≈ log2^30 ≈ 30

贪心算法

贪心本质

使用前提:1,贪心选择性质;;;2,最优子结构性质

贪心选择性质

整体最优解,可以用一系列局部最优解表示。

应用同一规则,将原问题变为一个相似的,规模更小的子问题,而后的每一步都是当前最优选择,这种选择依赖于已做出的选择。

贪心解决的问题在程序运行中,无回溯过程

最有子结构性质:

一个问题的最优解包含其子问题的最优解。

例如S = {a1, a2, ..., ai, ..., an},通过贪心选出一个当前最优解{ai}后,转化为求解子问题S-{ai},如果原问题的最优解包含子问题的最优解,则说明该问题满足最优子结构性质

贪心入门+10道例题+解析代码_第3张图片

求解步骤:

(1)贪心策略

指定贪心策略,选择当前看上去最好的一个。

比如挑选苹果,如果你认为个头大的是最好的,那么每次都从苹果堆选一个最大的作为局部最优解,贪心策略就是选择当前最大的苹果。

如果你认为最红的苹果是最好的,那么每次都从苹果堆取一个最红的,贪心策略就是选择最红的苹果。

根据求解目标的不同,贪心策略也不同

(2)局部最优解

根据贪心策略,一步步得到局部最优解。

比如第1次选一个最大的苹果放起来,记为a1;

第2次再从剩下苹果选一个最大的苹果放起来,记为a2......

(3)全局最优解

把所有局部最优解合成原问题的一个最优解{a1, a2, ......}

最优装载问题

有一天,海盗们截获了一艘装满各种各样古董的货船,每件古董都价值连城,一旦打碎就失去了价值。虽然海盗船足够大,但载重为c,每件古董重量为wi,海盗们绞尽脑汁要把尽可能多的宝贝装上船,该怎么办呢?

(看清题意,是装的数量最大,而非重量最大)

1,问题分析

装载的物品尽可能多(数量最多),船的容量固定,那么优先装重量小的物品。

采用重量最轻者先装,从局部最优达到全局最优。

2,算法设计

(1)载重定值为c,wi越小,可装载的古董数量n越大。依次选择最小重量古董,直到不能装入

(2)把n个古董按重量升序排序,根据贪心策略尽可能多的选出前i个古董,直到不能继续装入。

此时装入的古董数量就是全局最优解

3,完美图解

贪心入门+10道例题+解析代码_第4张图片

4,算法实现

用一维数组w[] 存储古董重量

(1)按重量升序排序,C++ sort()函数,头文件#include

sort(begin, end); 
//参数begin和end表示一个范围,分别为待排序数组的首地址和尾地址,默认升序

本例即sort(w, w + n);

(2)按照贪心策略找最优解

ans记录已装载数量,tmp代表装载到船上的古董重量,两个变脸初始化为0...

依次检查每个古董,tmp += 该古董重量,<= 载重c,则令ans++,否则退出

double tmp = 0.0;
int ans = 0; //tmp已装载总重量,ans为已装载个数
for(int i = 0; i < n; ++i) {
    tmp += w[i];
    if(tmp <= c)
        ans++;
    else 
        break;
}
cout<

5,算法分析

时间复杂度

sort函数平均时间复杂度O(nlogn),输入和贪心策略求解的两个for语句的时间复杂度均为O(n),总时间复杂度O(nlogn)

空间复杂度

程序中使用tmp, ans等辅助变量,空间复杂度O(1)

贪心刷题

可以看看我当初刷的12道贪心题目(含链接和AC代码),当初啥也不会,结构体都不会,瞎折腾

(58条消息) C++蓝桥杯贪心算法_千帐灯无此声的博客-CSDN博客

下面是我从网上找的新题目

1832: [NewOJ Week 9] 配对

P1832 - [NewOJ Week 9] 配对 - New Online Judge (ecustacm.cn)

标签:基础题,贪心

贪心入门+10道例题+解析代码_第5张图片

贪心入门+10道例题+解析代码_第6张图片对A,B都升序排序,然后用当前A的最大值配对B的最小值,配对完这次后,这两个糖果就可以“丢掉了”,后续也是按这个规则,那么一系列局部最优解就能得出全局最优解

额外的测试

1
10
1 9 10 20 11 13 15 29 33 2
1 2 3 4 5 6 7 8 9 10
34

结果AC  0%,原来是多组测试,不要忘了第23行回车

cout<

AC  代码

#include
#include //sort()
using namespace std;
const int maxn = 2e4 + 10;
long long a[maxn], b[maxn];
int main()
{
    int t, n;
    cin>>t;
    while(t--) {
        cin>>n;
        for(int i = 0; i < n; ++i)
            cin>>a[i];
        for(int i = 0; i < n; ++i)
            cin>>b[i];
        sort(a, a + n);
        sort(b, b + n); //升序排序
        long long Max = 0;
        //最小的红糖果和最大的蓝糖果依次配对,
        //得到的最大甜度值最小
        for(int i = 0, j = n - 1; i < n; ++i, --j)
            Max = max(a[i] + b[j], Max); //取最大甜度
        cout<

1785: [NewOJ Contest 10] 剪绳子

P1785 - [NewOJ Contest 10] 剪绳子 - New Online Judge (ecustacm.cn)

标签:基础题,贪心

贪心入门+10道例题+解析代码_第7张图片

任意大于等于5的正整数,可以拆成2和3的组合,而且乘积还变大了

比如

2 = 2, 3 = 3, 4 = 2 + 2, 5 = 2 + 3, 6 = 3 + 3
7 = 3 + 2 + 2, 8 = 3 + 3 + 2, 9 = 3 + 3 + 3...

显而易见,3越多,乘积越大,比如3 * 3 * 2 > 2 * 2 * 2 * 2等等

至于证明,用到了算几不等式

贪心入门+10道例题+解析代码_第8张图片

贪心入门+10道例题+解析代码_第9张图片显然1000个数,332个3,2个2刚刚好

1000 = 332 * 3 + 2 * 2,答案就是3^332 + 4

AC  代码

#include
using namespace std;

int main()
{
    long long ans = 1;
    //边乘边取余
    for(int i = 0; i < 332; ++i)
        ans = ans * 3 % 1000000007; //3^332, 还剩4
    cout<
620946522

P1589 泥泞路

P1589 泥泞路 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

标签:普及-,贪心,数学

贪心入门+10道例题+解析代码_第10张图片

先小到大排序,按起点终点都行,由题意,泥路不存在重合

额外的测试

3 10
5 25
40 81
85 90
7

4 1000
1 28
33 104
104 111
1002 1003
2

AC  60%  代码

调了很久才AC  60%

#include
#include//sort()
using namespace std;
int ans; //需要的木板数

struct rode
{
    int fir, sec; //每段路起点, 终点
};

bool cmp(rode x, rode y)
{
    return x.fir < y.fir; //按起点升序排序
}

int main()
{
    struct rode a[10010];
    int n, L;
    cin>>n>>L;
    //输入n段泥泞的路
    for(int i = 0; i < n; ++i)
        cin>>a[i].fir>>a[i].sec;
    sort(a, a + n, cmp); //排序
    //遍历每一段路
    int len , num;
    for(int i = 0; i < n; ++i) {
        len = a[i].sec - a[i].fir; //泥路长度
        num = (len + L - 1) / L; //当前路段增加木板数
        ans += num;
        if(num != 0) //如果加了木板, 取较大值
            a[i + 1].fir = max(a[i + 1].fir, a[i].fir + L * num); //可能的新起点
    }
    cout<

我也不知道哪里错了,懒得debug了

换个思路:用now表示已经覆盖的最远距离一块一块木板慢慢来

AC  代码

#include
#include//sort()
using namespace std;
int ans; //需要的木板数

struct rode
{
    int fir, sec; //每段路起点, 终点
};

bool cmp(rode x, rode y)
{
    return x.fir < y.fir; //按起点升序排序
}

int main()
{
    struct rode a[10010];
    int n, L;
    cin>>n>>L;
    //输入n段泥泞的路
    for(int i = 0; i < n; ++i)
        cin>>a[i].fir>>a[i].sec;
    sort(a, a + n, cmp); //排序
    //遍历每一段路
    int now = a[0].fir; //now已经覆盖的最远距离
    for(int i = 0; i < n; ++i) {
        now = max(now, a[i].fir); //更新
        while(a[i].sec > now) { //泥路终点大于当前位置
            ans++;
            now += L; //当前位置前进一块木板的距离
        }
    }
    cout<

代码第29 - 31行,一块一块木板来,防止漏掉某些情况 

P1208 [USACO1.3]混合牛奶 Mixing Milk

P1208 [USACO1.3]混合牛奶 Mixing Milk - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

标签:贪心,USACO,普及- 

贪心入门+10道例题+解析代码_第11张图片

贪心入门+10道例题+解析代码_第12张图片

对单价升序排序后遍历

AC  代码

#include
#include //sort()
using namespace std;
typedef long long LL;

struct milk
{
    LL price, all; //单价和产量
}a[5010];

bool cmp(milk x, milk y)
{
    return x.price < y.price;
}

int main()
{
    LL n, m, ans = 0; //剩余需要的数量n, 农民人数m, 最小费用ans
    cin>>n>>m;
    for(int i = 0; i < m; ++i)
        cin>>a[i].price>>a[i].all;
    sort(a, a + m, cmp); //单价升序排序
    //遍历不同单价的农民
    for(int i = 0; i < m; ++i) {
        if(n >= a[i].all) {
            ans += a[i].all * a[i].price;
            n -= a[i].all; //表示剩余需要的牛奶
        }
        else { //如果不需要那么多
            ans += n * a[i].price;
            n = 0;
        }
    }
    cout<

P4995 跳跳!

P4995 跳跳! - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

标签:普及/提高-,贪心,排序

贪心入门+10道例题+解析代码_第13张图片

贪心入门+10道例题+解析代码_第14张图片

升序排序后...

显然,先从地面跳到最高的石头,再从(未跳过的)最高的跳到(未跳过的)最矮的...

因为题目说了,每个石头只跳1次

采用i, j两个相向的游标即可实现

贪心入门+10道例题+解析代码_第15张图片

AC  代码

#include
#include //sort()
using namespace std;
typedef long long LL;
LL a[310];

LL resume(LL x, LL y) //耗费体力值
{
    return (x - y) * (x - y); //保证 x >= y
}

int main()
{
    LL n, ans = 0;
    cin>>n;
    for(LL i = 1; i <= n; ++i) //1开始输入
        cin>>a[i];
    sort(a + 1, a + n + 1); //升序排序
    //遍历, 先++i, 再++j, 再... 直到i == j
    LL i = 0, j = n, num = 1;
    while(i != j) {
        ans += resume(a[j], a[i]);
        if(num % 2 == 1) i++;
        else j--;
        num++;
    }
    cout<

P1094 [NOIP2007 普及组] 纪念品分组

P1094 [NOIP2007 普及组] 纪念品分组 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

标签:贪心,排序,普及-

贪心入门+10道例题+解析代码_第16张图片

贪心入门+10道例题+解析代码_第17张图片

(先升序排序)

这题和上题差不多,略微区别的是,两个游标的移动方式(都是相向移动)

上一题是i++ 和 j--轮着来

本题是先确定初始的 i,然后一直 j--,直到遇到第一个满足 a[i] + a[j] <= w的

把结果处理了,i++,继续j--,直到遇到下一个满足 a[i] + a[j] <= w...

额外测试

100
5
70
40
60
60
60
4

AC  代码

#include
#include //sort()
using namespace std;
int a[30010];

int main()
{
    int n, w;
    cin>>w>>n;
    for(int i = 0; i < n; ++i)
        cin>>a[i];
    sort(a, a + n); //升序排序
    int i = 0, j = n - 1, ans = 0;
    while(i <= j) {
        while(a[i] + a[j] > w && i != j) {
            j--; //直到满足两件放一组的条件
            ans++; //1件纪念品占1组
        }
        j--;
        i++;
        ans++; //2件占1组, 当然最后一次i == j时1件占1组
    }
    cout<

P1199 [NOIP2010 普及组] 三国游戏

P1199 [NOIP2010 普及组] 三国游戏 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

标签:贪心,博弈论,普及//提高-

题目有点长,就不放了

思路

由于计算机自动选下一个和人配对最高的武将,(人先手)所以每一行最高的组合,人取不到,计算机也取不到,所以,人能必胜的条件,就是取到所有行第二高的组合中,的最大值

注意点补充

1,500*500 = 2.5 * 10^5,数据量达到1e5,用scanf()

2,对照下图去输入和排序,防止输入错误或者排序错误

贪心入门+10道例题+解析代码_第18张图片

AC  代码

#include
#include //scanf()
#include //sort()
using namespace std;
int a[510][510];

int main()
{
    int n, now = 0;
    scanf("%d", &n);
    for(int i = 1; i <= n - 1; ++i) //1~n-1
        for(int j = i + 1; j <= n; ++j) { //i+1 ~ n
            cin>>a[i][j];
            a[j][i] = a[i][j]; //无向, 对照草稿图, 输入完整
        }
    //人先选
    //最大都选不到, 人选最大的次大必胜, 不存在输的情况
    int ans = 0;
    for(int i = 1; i <= n; ++i) {
        sort(a[i] + 1, a[i] + 1 + n); //起点和终点地址
        ans = max(ans, a[i][n - 1]); //取次大最大值
    }
    cout<<1<
4
256032391 44492372 820892564
72613801 404834202
13297961
1
404834202

P2672 [NOIP2015 普及组] 推销员

P2672 [NOIP2015 普及组] 推销员 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

标签:提高+/省选-,贪心,线段树,树状数组

贪心入门+10道例题+解析代码_第19张图片

本题在模拟清楚题意的基础上,借助贪心 + 前缀和 + 预处理,不用线段树,树状数组也能完成

贪心入门+10道例题+解析代码_第20张图片

好像只用贪心能解决

注意,下面的洛谷题解中,关于h[i]的描述不够严谨,不是后 i 个,而是第 i ~ 第 n 个

也就是第 i 到 最后一个住户(注意h[i]预处理是从n遍历到1的)

终点观察样例2

贪心入门+10道例题+解析代码_第21张图片

贪心入门+10道例题+解析代码_第22张图片

思路

只算一次最远路程,首先模拟一次样例2,结合链接的例子模拟一遍,

主要看它的例子

(71条消息) P2672 [NOIP2015 普及组] 推销员_c++ 推销员_smooth的博客-CSDN博客

 再看这个题解 P2672 【推销员】 - Rainy7の灯塔 - 洛谷博客 (luogu.com.cn)

蓝题对我来说太难了,我只是努力看懂题解,然后自己敲一遍

1,按疲劳值降序(大到小)排序       

2,sum(a[k])(1≤k≤X)+s[j]∗2

取前k个最大疲劳值的前缀和 + 前k个最大疲劳值中最大距离 * 2

3,推销员每走远一点,舍去前X大的最小值(即第X大),看能不能通过走更远来获取更大的疲劳值  (为什么只舍去一个呢,因为已经疲劳值大到小排序了,省略100字证明.........)

贪心入门+10道例题+解析代码_第23张图片

以上截图来自洛谷题解博客

建议结合第一个链接的例子,来模拟这个过程,助于理解

核心思想: 只需舍去最小值来走更远,无需舍去更多数来走更远  

v[i].s表示距离,v[i].a表示疲劳值,q[i]表示前i个最大值,h[i]表示后i个最大值

sum[i]表示疲劳前缀和(不包含距离的因素,只是推销产品的疲劳值)

q[i]是前i个,在X和X之前的住户,所以

最后输出时,取max(sum[i] + q[i], sum[i - 1] + h[i]); 就应用了只舍去最小值的思想

sum[i] + q[i] 表示(前i个推销疲劳值的前缀和) + (前i个距离的最大值)(由题可知,只取距离最大值)

sum[i - 1] + h[i] (舍弃掉前X大中的最小值后),+ (第i到最后一个 推销的疲劳值 和 最大距离的疲劳值)

拿第一个链接的数据举个例子(按疲劳值大到小排序后)

距离 4 1 3 5 2 6
疲劳值 5 4 3 3 2 1

当 X = 2,sum[i] + q[i] = (5 + 4) + (4 * 2),注意q[i]只包含距离疲劳值

而 sum[i - 1] + h[i] = (5) + (6 * 2 + 1),而h[i]包括距离疲劳值 + 销售疲劳值

贪心入门+10道例题+解析代码_第24张图片 再放一次,多看看

数据范围不超过10^9,可以int

最后再解释下,有人有疑问,为什么h[i]可以直接sum[i - 1] + h[i]呢

万一最大值没有包含在前i个里呢

因为h[i]本身是从n开始遍历的,当它遍历到1时,已经包括了整个数组中疲劳值(距离+推销)的最大值

会有这个疑问的原因是,题解描述不够严谨,不应该说后i个,应该说第 i 到第n个

先模拟一遍过程,再想到直接暴力会TLE,然后用前缀和优化....题解看了很久很久很久

(看难题题解,没什么好办法,多模拟几遍,然后理解透了就自己敲一遍,对我来说确实难)

AC  代码

#include
#include //scanf()
#include //sort()
using namespace std;
//sum[i]是推销的疲劳值的前缀和
//q[i]是前i个距离的疲劳值的最大值
//h[i]是第i到第n个 距离 + 推销的疲劳值最大值
int sum[100010], q[100010], h[100010];

struct node
{
    int s, a; //s表示距离distance, a表示推销的疲劳值
}v[100010];

bool cmp(node x, node y)
{
    return x.a > y.a; //推销的疲劳值大到小排序
}

int main()
{
    int n;
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i)
        scanf("%d", &v[i].s); //读入距离
    for(int i = 1; i <= n; ++i)
        scanf("%d", &v[i].a); //读入推销的疲劳值
    sort(v + 1, v + 1 + n, cmp); //传入地址和比较函数
    //前缀和 + 预处理
    for(int i = 1; i <= n; ++i)
        sum[i] = sum[i - 1] + v[i].a; //推销疲劳值前缀和
    for(int i = 1; i <= n; ++i)
        q[i] = max(q[i - 1], 2*v[i].s); //前i个住户最大距离疲劳值预处理
    for(int i = n; i >= 1; --i)
        h[i] = max(h[i + 1], 2*v[i].s + v[i].a); //第i ~ n个住户总疲劳值预处理
    //输出
    for(int i = 1; i <= n; ++i)
        cout<

P1080 [NOIP2012 提高组] 国王游戏

P1080 [NOIP2012 提高组] 国王游戏 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

标签:普及+/提高,贪心,高精度,排序 

贪心入门+10道例题+解析代码_第25张图片

贪心入门+10道例题+解析代码_第26张图片

考虑到不会高精度,专门花了一天时间学习5种高精度

哎,本题难点不在于贪心,而在于处理高精度乘除法的输入输出

如果给定2个数确实好处理,但是多次输入就懵了

高精度_加减乘除(理论+模板+例题)_千帐灯无此声的博客-CSDN博客

思路

n = 10^4, a, b = 10^4,最大可达10^40000,长度达40000位的字符串,显然是高精度乘法

然后就是贪心:

首先看清楚题目:

1,左手上数字累乘的前提是,“排在该大臣前面的所有人”,不包括他自己

2,请看截图,需要注意的是,"a, b为整数,且 > 0",整数这个点要注意

a. 贪心的实现

贪心入门+10道例题+解析代码_第27张图片

所以大臣1排在大臣2前面更优的条件是:

a1 * b1 < a2 * b2

最终按 a * b从小到大排序

(按这个思路,显然,最后一个大臣得到的金币最多,只算最后一个大臣即可)

b. 高精度的实现

乘法部分:高精 * 低精度(2层for循环)(乘法需要乘n - 1次)

除法部分:高精 / 低精度(逐位试商法)(除法只需最后除1次)

请看博客:高精度_加减乘除(理论+模板+例题)_千帐灯无此声的博客-CSDN博客

c. 如何读入

下标从1开始,具体为什么,请看博客中的“大整数乘法”

不看题解代码,了解思路后自己敲,速度真的很慢。。。而且最后还容易TLE

未  AC  代码 

这道题做到一半,阳了,没办法

贪心入门+10道例题+解析代码_第28张图片

基本思路全了,就差每一部分的debug,不想做了,放这先

至少,贪心的思路模拟了,高精度也入门了

#include
#include //sort()
#include //scanf()
#include //memset()
using namespace std;
//最多40000位数字
int a[40010], aa[20]; //a[]左手累乘结果, aa[]每个大臣左手金币数更新进去
int tmp[40010]; //高精度乘法的中间数组

struct node
{
    int l, r; //左手右手金币
}num[10010]; //num每个大臣左右手

bool cmp(node x, node y)
{
    return x.l*x.r <= y.l*y.r; //左手*右手, 小到大排序
}

//将单个低精度数字更新到数组里, 低位在前
void in(int *a, int x)
{
    for(int i = 1;; ++i) { //下标1开始
        if(!x) break; //x输入完毕
        a[i] = x % 10;
        x /= 10;
        a[0]++; //a[0]保存左手数字长度
    }
} //全局变量中a[0]表示累乘长度, aa[0]表示右手数字长度

void cpy(int *x, int *y) //数组x拷贝到数组y上
{
    for(int i = 0; i <= 40000; ++i)
        y[i] = x[i];
}

int main()
{
    int n;
    scanf("%d", &n);
    for(int i = 1; i <= n + 1; ++i) //下标1表示国王
        scanf("%d%d", &num[i].l, &num[i].r);
    sort(num + 2, num + n + 2, cmp); //国王不参与排序
    //先将国王左手数字存入a[]
    in(a, num[1].l);

    //遍历每个大臣左手
    for(int k = 2; k <= n; ++k) { //k == n就是第 n - 1个大臣
        in(aa, num[k].l); //低位开始存入数组aa[]
        //清空中间数组
        memset(tmp, 0, sizeof(tmp));

        //高精度乘法部分
        for(int i = 1; i <= a[0]; ++i)
            for(int j = 1; j <= aa[0]; ++j) {
                tmp[i + j - 1] += a[i] * aa[j]; //按位相乘
                //每次乘完后进位
                tmp[i + j] += tmp[i + j - 1] / 10;
                tmp[i + j - 1] %= 10; //保留个位
            }
        cpy(tmp, a); //tmp[]拷贝到a[]
        //更新a[]大小, 最大可达a[0] + aa[0]
        for(int i = a[0] + aa[0]; i >= 1; --i)
            if(a[i]) { //到达最高位
                a[0] = i; //得到当前数组大小
                break;
            }
    }

    //高精 / 低精: 逐位试商法
    //a[]就是被除数
    long long x = 0, b = num[n + 1].r; //x余数, b商
    for(int i = a[0]; i >= 1; --i) { //高位开始
        a[i] = (x * 10 + a[i]) / b; //商
        x = (x * 10 + a[i]) % b; //余数
    }
    //输出商, 即最后一个大臣的金币数
    int kk = a[0];
    for(int i = kk; i >= 1; --i)
        if(a[i]) break; //排除前导0
    for(int i = kk; i >= 1; --i)
        cout<

总结

先立业,后成家,没有一定的经济能力,吃个饭都抠抠索索,旅个游都犹犹豫豫,活着就真的没意思

起码得稳定月入8000以上,存款30万以上,有着足够温饱的副业,即使失业了也没必要太焦虑

至于房子,现在还买房的怕不是上辈子毁灭宇宙了

稳定收入,温饱副业,一定存款,单身小日子就真香

你可能感兴趣的:(《算法训练营入门篇》,算法,贪心算法,蓝桥杯)