目录

1、家庭作业

2、加工生产调度

3、喷水装置

4、种树

5、活动安排3

6、线段

7、数列分段2

8、数列极差问题

9、糖果传递

10、钓鱼2

11、排队接水

12、智力大冲浪

13、均分纸牌


贪心算法常见解法 原文链接 http://t.csdn.cn/7jwOQ

1、家庭作业

题意:老师在开学第一天就把作业都布置了,每个作业都有一个截止时间和一些相应的学分,但是只有在截止日期内完成才可以获得相应的学分,超时完成不会这部分学分,问:如何安排作业的完成顺序使得最终的得分最高(严格要求每个作业必须需要单独的一天来完成)

思路:目标是得到最多的学分,一天内只能完成一项作业,所以为了最大值,所以要优先解决学分高的作业,所以首先按照学分来进行降序排序,由于数据较大,使用常规的模拟的话会超时,这里可以使用并查集,(常规的方法的需要一直向前模拟,并查集中使用路径压缩可以极大的减少模拟的此时,减少大量的无用模拟)

代码:

#include <bits/stdc++.h>
using namespace std;const int N = 1e6 + 5;
int st[N];
int f[N];
int n;
bool flag;//并查集的初始化
void init() {for (int i = 1; i <= n; i ++ ) f[i] = i;
}struct node {int time, score;
}s[N];//降序排列
bool cmp(node a, node b) {return a.score > b.score;
}int find(int x) {if(f[x] <= 0) return 0;else if(f[x] == x) {flag = true;f[x] -- ;return f[x];}else {return f[x] = find(f[x]);//进行路径压缩 }
}int main() {scanf("%d", &n);init();int a, b;for (int i = 1; i <= n; i ++ ) {scanf("%d%d", &a, &b);s[i] = {a, b};}sort(s + 1, s + 1 + n, cmp);int ans = 0;for (int i = 1; i <= n; i ++ ) {flag = false;f[s[i].time] = find(s[i].time);if(flag) ans += s[i].score;}printf("%d\n", ans);return 0;
} 

2、加工生产调度(贪心)

题意:有n个部件需要进行加工,每一个部件都需要先经过A工序,然后经过B工序才行,每个部件在A和B工序的处理时间已知,问:如何安排加工顺序可以使得总处理时间最少

思路:在第一个物品进行A工序时不会出现时间重复的物体,最后一个物品在进行B工序时同样不会有时间重复的情况,中间的物品的话会出现时间重复问题,一般是对于第K各部件的A工序的处理时间会和K - 1的部件的B工序处理时间会重复,第K个部件的B工序的处理时间会和第K + 1个部件的处理时间重复,甚至还会K - 2 和K + 2有时间重复问题,中间的部件加工会出现排序不同时间相同的情况,

处理方法:将每个物件按照min(A_time, B_time) (最小处理时间)来进行升序排序,当第i个部件的最小处理时间是A的处理时间时,将其依次放在前面,若是最小处理时间是B工序的处理时间则将其放在后面,具体的证明如下链接 ,依照这个方法可以得到最优排序,在通过模拟得到最小的处理时间(这个模拟很经典)

证明链接 : http://t.csdn.cn/Yjy8E

代码:

#include <bits/stdc++.h>
using namespace std;const int N = 1e3 + 5;
int a[N];
int b[N];
int n;
int book[N];struct node {int w, id;
}s[N];bool cmp(node a, node b) {return a.w < b.w;
}int main() {scanf("%d", &n);for (int i = 1; i <= n; i ++ ) scanf("%d", &a[i]);for (int i = 1; i <= n; i ++ ) {scanf("%d", &b[i]);s[i] = {min(a[i], b[i]), i};}sort (s + 1, s + 1 + n, cmp);int l = 0, r = n + 1;for (int i = 1; i <= n; i ++ ) {if(s[i].w == a[s[i].id]) {book[ ++ l] = s[i].id;}else {book[ -- r] = s[i].id;}}//模拟,很重要int begin, end;begin = end = 0;for (int i = 1; i <= n; i ++ ) {begin += a[book[i]];if(begin > end) end = begin;end += b[book[i]];} printf("%d\n", end);for (int i = 1; i <= n; i ++ ) {printf("%d", book[i]);if(i == n) printf("\n");else printf(" ");}return 0;
} 

3、喷水装置3

题意:有一个长L,宽M的草地,并且中间有有一些喷水装置,每个喷水装置都有相应的位置并且每个喷水装置都有一个喷水半径(具体见图),问:计算最少需要开多少个喷水装置客户以覆盖到整个草地

思路:

每个相邻喷水之间需要满足图中的样子才可以全部覆盖一段草地面积,对于重复部分不会对草地覆盖起到任何作用,所以对于一个使用的喷水装置实际的覆盖面积是绿色的面积,所以此时问题转换为经典的线段覆盖性问题,并且每个喷水装置的半径R >= w / 2 才可以让草地的上下的面积

由图可得两个方程 len1 ^ 2 = R1 ^ 2 - (w / 2) ^ 2 和 len2 ^ 2 = R2 ^ 2 - (w / 2) ^ 2 由此可以得到每个喷水装置对应的左右实际需覆盖的区间

代码:

#include <bits/stdc++.h>
using namespace std;const int N = 1e5 + 5005;struct node {double l, r;
}s[N];double n, L, w;//记得标记成双精度, 进行同精度之间的大小比较 bool cmp(node a, node b) {return a.l < b.l;
}int main() {int t;scanf("%d", &t);while (t -- ) {cin >> n >> L >> w;int ans = 0;int mid, r;for (int i = 1; i <= n; i ++ ) {scanf("%d%d", &mid, &r);if(r > w / 2) {s[ ++ ans].l = mid - sqrt(r * r - w * w / 4);s[ans].r = mid + sqrt(r * r - w * w / 4);}}sort (s + 1, s + 1 + ans, cmp);double begin = 0;double end = -1;int res = 1;for (int i = 1; i <= ans; i ++ ) {if(s[i].l > begin) {if(end < s[i].l) {break;}begin = end;res ++ ;}end = max(end, s[i].r);if(end >= L) break;}//       cout << end << " " << L << endl;if(end >= L) printf("%d\n", res);else printf("-1\n");}return 0;
} 

4、种树

题意:在一段1 到 n 中,给定h个区间,并且每个区间中的树的数目需要大于等于T棵(每个区间都有自己的限定数目),问:最少需要多少棵树可以满足所以的需求

思路:对于区间之间会出现重复的情况,目的是使用最少的树来满足所有的需求,所以要尽量的将树种在区间的重复区间,所以对于所以的区间进行倒序排序(对于贪心问题的排序可能涉及到证明方面的问题,我不是很清楚,希望有大神帮我解惑,感谢),为了更多在重复区间种树,所以从区间的左边想右边种树,一次进行模拟操作即可

代码:

#include <bits/stdc++.h>
using namespace std;const int N = 2e5 + 7;struct node {int l, r;int num;
}s[N];int n, h;
int vis[N];//倒着排序
bool cmp(node a, node b) {if(a.l == b.l) return a.r > b.r;return a.l > b.l;
}int main() {cin >> n;cin >> h;memset(vis, 0, sizeof vis);int a, b, c;for (int i = 1; i <= h; i ++ ) {scanf("%d%d%d", &a, &b, &c);s[i] = {a, b, c};}sort (s + 1, s + 1 + h, cmp);int ans = 0;for (int i = 1; i <= h; i ++ ) {int res = 0, num = 0;for (int j = s[i].l; j <= s[i].r; j ++ ) {if(vis[j]) {s[i].num -- ;}}for (int j = s[i].l; j <= s[i].r; j ++ ) {if(s[i].num <= 0) break;//特别注意 if(!vis[j]) {vis[j] = 1;ans ++ ;s[i].num -- ;}}}printf("%d\n", ans);return 0;
} 

5、活动排序3

题意:已知在1 到 N区间中中安排活动,活动必须单独使用某一天, 在一天中不能同时进行多个活动,问:如何排序可以使得在1 到 N 的时间中安排更多的活动

思路:对于活动安排,上一个活动结束的早,后面可以安排的活动就也多,所以可以按照活动的结束时间进行排序,然后依次安排活动可以安排最多的活动

代码:

#include <bits/stdc++.h>
using namespace std;const int N = 1e3 + 5;struct node {int l, r;
}s[N];
int n;bool cmp(node a, node b) {return a.r < b.r;
}int main() {scanf("%d", &n);int l, r;for (int i = 0; i < n; i ++ ) {scanf("%d%d", &l, &r);s[i] = {l, r};}   int ans = 0;sort (s, s + n, cmp);//   for (int i = 0; i < n; i ++ ) {
//      cout << s[i].l << " " << s[i].r << endl;
//  }
//  cout << endl;int temp = -1;for (int i = 0; i < n; i ++ ) {if(s[i].l >= temp) {ans ++ ;temp = s[i].r;}}printf("%d", ans);return 0;
}

6、线段

题意:经典的线段覆盖问题,在n个区间中选取最多的区间使得区间两两不想交

思路:贪心中的区间调度问题,要使选取的线段数量最多,只需要在满足与前一个线段不重合的情况下,每次选择右端点相对靠前的线段,这样能使数轴上余下的长度更长,选择的余地更大

代码:

#include <bits/stdc++.h>
using namespace std;const int N = 1e6 + 5;struct node {int l, r;
}s[N];int n;bool cmp(node a, node b) {if(a.r == b.r) { // 这里的判断对答案的搜索没有影响return a.l > b.l;}return a.r < b.r;
}int main() {scanf("%d", &n);int l, r;for (int i = 0; i < n; i ++ ) {scanf("%d%d", &l, &r);s[i] = {l, r};}   sort (s, s + n, cmp);//    for (int i = 0; i < n; i ++ ) {
//      cout << s[i].l << " " << s[i].r << endl;
//  }int temp = -1;int ans = 0;for (int i = 0; i < n; i ++ ) {if(s[i].l >= temp) {ans ++ ;temp = s[i].r;}}printf("%d", ans);return 0;
} 

7、数列分段

题意:将n段数列分成连续的几段,并且每一段的总和不超过M,问:最少划分段数是多少

思路:有是分成连续的段数,所以直接按照正常顺序进行分段,若加上这一段该总段总和不超过M,直接将该点放在该段中即可,若是超过了M,则另起一段

代码:

#include <bits/stdc++.h>
using namespace std;#define inf 0x3f3f3f3f
const int N = 1e5 + 5;
int a[N];
int n, m;int main() {scanf("%d%d", &n, &m);for (int i = 0; i < n; i ++ ) {scanf("%d", &a[i]);}//分成连续的若干段a[n] = 1e9 + 10;int len = 0;int ans = 1;for (int i = 0; i < n; i ++ ) {if(len + a[i] <= m) {len += a[i];}else {len = a[i];ans ++ ;}}printf("%d", ans);return 0;
} 

8、数列极差问题

题意:已知一个数组,每次选出其中的两个数 a 和 b,合并a * b + 1,之后将其放在数组中,a和b从数组中删除,不断的操作之后使得数组中仅剩一个数,问:通过这种连续的操作得到的最后一个数的最大值和最小值的差值是多少

思路:

每次合并最小的两个最后得到的是最大的,而每次合并最大的两个的时候最后得到的是最小的。
例如有6个数,即8 6 5 9 7 1
则min求值过程为:    max求值过程为:
73  7  6  5  1     6  6  7  8  9
512  6  5  1       37  7  8  9
3073  5  1        37  57  9
15366  1          334  57
15367             19039
ans=19039-15367=3672

代码:

#include <bits/stdc++.h>
using namespace std;typedef long long ll;
priority_queue<ll, vector<ll>, greater<ll> > q;
priority_queue<ll, vector<ll>, less<ll> > qq;
ll x;int main() {int n;while (scanf("%d", &n)) {if(n == 0) break;for (int i = 1; i <= n; i ++ ) {scanf("%lld", &x);q.push(x);qq.push(x);}  while (q.size() >= 2) {ll x = q.top(); q.pop();ll xx = q.top(); q.pop();ll ans = (x * xx) + 1;q.push(ans);}ll sum = q.top();while (qq.size() >= 2) {ll x = qq.top(); qq.pop();ll xx = qq.top(); qq.pop();ll ans = (x * xx) + 1;qq.push(ans);}ll sum1 = qq.top();cout << sum - sum1 << endl;}return 0;
}

9、糖果传递

题意:

思路:(数论题)y总讲得很好,并且还给出了最优解的证明,建议大家去学习一下

链接:https://www.acwing.com/video/2344/

代码:

#include <bits/stdc++.h>
using namespace std;typedef long long ll;
const int N = 1e6 + 5;int n;
ll s[N], c[N];int main() {scanf("%d", &n);for (int i = 1; i <= n; i ++ ) {scanf("%lld", &s[i]);s[i] += s[i - 1];}ll b = s[n] / n;int k = 0;for (int i = 1; i < n; i ++ ) c[k ++ ] = i * b - s[i];c[k ++ ] = 0;nth_element(c, c + k / 2, c + k);ll res = 0;for(int i = 0; i < k; i ++ ) {res += abs(c[i] - c[k / 2]);}printf("%lld\n", res);return 0;
}

10、钓鱼

思路:洛谷链接:https://www.luogu.com.cn/problem/solution/P1717 (第一个解析中的动态规划的做法有些问题,洛谷的数据比较少,建议都去试试)

11、智力大冲浪

题意:给定一些游戏的截止时间和未完成有所扣的分,在给定其分数的情况下,算出最大的最终得分

思路:和上面家庭作业的思路相同(使用并查集来降低模拟时间)

代码:

#include <bits/stdc++.h>
using namespace std;const int N = 1e3 + 5;
int a[N], w[N];
int m, n;
int f[N];
bool flag;void init() {for (int i = 1; i <= n; i ++ ) f[i] = i;
}struct node {int time, value;
}s[N];bool cmp(node a, node b) {return a.value > b.value;
}int find(int x) {if(f[x] <= 0) return 0;else if(f[x] == x) {flag = true;f[x] -- ;return f[x];}else {return f[x] = find(f[x]);}
}int main() {scanf("%d", &m);scanf("%d", &n);init();for (int i = 1; i <= n; i ++ ) scanf("%d", &a[i]);for (int i = 1; i <= n; i ++ ) {scanf("%d", &w[i]);s[i] = {a[i], w[i]};}sort (s + 1, s + 1 + n, cmp);for (int i = 1; i <= n; i ++ ) {flag = false;f[s[i].time] = find(s[i].time);if(!flag) {m -= s[i].value;//            cout << s[i].value << endl;} }printf("%d\n", m);return 0;
}

12、均分纸牌

题意:对于每一堆纸牌,通过左右给予使得对纸牌的数目为平均值,第1堆只能给第2堆,第2堆可以给第1堆和第3堆......第n - 1堆可以给第n堆也可以给第n - 2堆,第n对堆只能给第n -  1堆

思路:(糖果传递的简化版)题目中规定一定存在最优解,所以可以从头向后模拟寻找,若是第i堆不足平均值的话,所需的就是正值,若是大于平均值的话,转移值则是负值,最终一定会使得每一堆的纸牌数目为平均值

代码:

#include <bits/stdc++.h>
using namespace std;const int N = 1e3 + 5;
int a[N];
int n; bool cmp(int a, int b) {return a < b;
}int main() {scanf("%d", &n);int sum = 0;    for (int i = 1; i <= n; i ++ ) {scanf("%d", &a[i]);sum += a[i];}//题目规定一定存在最优解 sum /= n;int res = 0;for (int i = 1; i <= n; i ++ ) {if(sum == a[i]) continue;a[i + 1] += a[i] - sum;res ++ ;}printf("%d", res);return 0;
} 

第一周一本通贪心合集相关推荐

  1. ACM算法训练【贪心合集】

    贪心合集 1.区间选点(区间问题·贪心) 2.区间分组(区间问题·贪心) 3.区间覆盖(区间问题·贪心) 4.耍杂技的牛(权重相加问题·贪心) 5.合并果子(Huffman树) 6.排队打水(排序不等 ...

  2. 小萌库一周电影大合集

    小萌库( www.xiaomengku.com )  一周电影大合集 怪兽大学(Monsters University)-2013美国最新动画片BT下载.torrent.zip好朋友"毛怪& ...

  3. ACM第二周---周赛---题目合集.

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言 A - k-LCM (easy version) B - Base K C - [例题]一维前缀和 D - 最小新整数 ...

  4. 用简单的方法做整套UI(教程第一/二/三弹合集)

    http://bbs.66rpg.com/thread-329530-1-1.html http://v.tieba.baidu.com/p/2985559487 首先要准备两个工具,"美图 ...

  5. 吐血整理-周志华演讲合集

    前言: 这是整理了周志华老师几个演讲笔记的合集,很齐全啊哈哈哈哈,欢迎大家留言补充 "现在是大数据时代,但是大数据不等于大价值." 我们要从大数据里面得到价值的话,就必须要有一些有 ...

  6. 珍宝鸭的力扣练习(8):贪心算法练习合集

    1.贪心算法适用的问题 贪心策略适用的前提是:局部最优策略能导致产生全局最优解. 实际上,贪心算法适用的情况很少.一般,对一个问题分析是否适用于贪心算法,可以先选择该问题下的几个实际数据进行分析,就可 ...

  7. 20210322 :贪心思想力扣典型题目合集

    贪心思想力扣典型题目合集 写在前面 题目列表 思路分析 代码实现 写在前面 贪心的思想很多时候在于想到那个贪心的点上,而对徒手书写某些代码结构的能力并不做要求,个人认为需要的是你敏锐的意识到这个贪心的 ...

  8. UI UX 小提示合集 -- 第一集

    UI & UX 小提示合集 -- 第一集 17个小提示让你的设计瞬间升级 我了解设计一个既漂亮又实用的界面的过程 - 通常较长,而且需要反复修改.大多数设计师都有类似的经历. 不过,多年的经验 ...

  9. 大学“电路分析基础”试题合集第一章

    "电路分析基础"试题合集第一章         答案见文末 一.单项选择题(在每个小题的4个备选答案中,选出一个正确答案,并将正确答案的号码填入提干的括号内.每小题2分,共40分) ...

最新文章

  1. 如何用两个小时入门 Docker?
  2. 报告 | 从20世纪70年代至今,自动驾驶汽车的发展经历了哪些历史性的变革?
  3. Bash中的$*和$@的区别
  4. fail safe java_Java中快速失败(fail-fast)和安全失败(fail-safe)的区别?
  5. 让你的spring-boot应用日志随心所欲--spring boot日志深入分析
  6. 海尔微型计算机一键还原怎么操作,教你电脑一键还原怎么操作
  7. python解析库详解_PyQuery库详解
  8. 某大型银行深化系统之十四:技术架构
  9. 第三百九十一节,Django+Xadmin打造上线标准的在线教育平台—404,403,500页面配置...
  10. 众昂矿业:萤石行业发展四大趋势
  11. mysql 导出数据到txt文件_mysql 导出数据到txt文件
  12. Markdown基本语法介绍及cmd markdown下载
  13. 什么是CRM?多角度解析CRM系统
  14. Windows10黑体字体
  15. FL Studio 21中文版支持主题随心换,FL Studio 21Mac版新增对苹果M2/1家族芯片原生支持。
  16. 内容为王,如何打造爆款小红书笔记?
  17. 浅谈音视频开发入门基础及进阶资源分享
  18. 入职字节跳动一年,谈谈我的工作收获,另附入职面经
  19. 基于LiDAR里程计和先验地图的定位方法
  20. 岭南师范学院计算机二级查询,岭南师范学院教务管理系统入口http://d.lingnan.edu.cn/jwc/...

热门文章

  1. 好嗨哦!用Python制作斗图表情包
  2. 【重点】一文搞懂Raft算法
  3. 外汇VPS的优缺点体现在哪些方面
  4. 网页设置引导收藏按钮
  5. redis 主从 + 哨兵模式集群部署(3台机器)
  6. live2dviewer android,live2dviewerex最新版
  7. html 弃用浏览器记住密码,html页面禁止自动填充浏览器记住的密码
  8. python 火烧赤壁 编程题(L1 研发工程师)
  9. 安装 Android SDK
  10. es同步mysql方案_ES数据同步方案