@Dmaxiya
2020-12-12T07:43:12.000000Z
字数 8104
阅读 1517
Hello_World
贪心算法(又称贪婪算法)是指,在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,他所做出的是在某种意义上的局部最优解。
贪心算法不是对所有问题都能得到整体最优解,关键是贪心策略的选择,选择的贪心策略必须具备无后效性,即某个状态以前的过程不会影响以后的状态,只与当前状态有关。
以上复制自百度百科,详细内容可以点链接查看,由于贪心就是在每个子问题中尽可能地获得最大利益,不能说是一种算法,应该是一种解题思维,贪心经常和其他算法一起考察。
贪心的难点在于证明贪心思路的正确性,有些是显而易见的,而有些是需要一番推导的,这需要大量做题来熟悉。
今后的题解只说思路,不会具体介绍代码的每一步在做什么。
要在 天内做完 套练习,每套练习都有一个分值和截止日期 ,他完成每套练习都需要一天时间,如果第 份练习完成的时间超过截止日期,则会被扣 分,问他最少会被扣多少分。
可以知道一定是分数越高的练习越早安排日期来做,但是不一定是第一天做,如果分数高但是截止日期比较靠后,则会占用其他练习的截止日期,我们可以将分数高的题目先安排,安排在最靠近这个练习截止日期的、没有被安排做作业的那一天来做,如果没有这样的日期可以安排了,就不做这一份作业,等着扣分了。
#include <iostream>#include <cstdio>#include <cstdlib>#include <cmath>#include <climits>#include <cstring>#include <string>#include <vector>#include <list>#include <queue>#include <stack>#include <map>#include <set>#include <bitset>#include <algorithm>#include <functional>#include <iomanip>using namespace std;#define LL long longconst int maxn = 1000 + 100;struct Node {int day;int score;};bool operator<(const Node &a, const Node &b) {if(a.score == b.score) {return a.day > b.day;}return a.score > b.score;}int T, n, ans;Node node[maxn];bool vis[maxn];int main() {#ifdef LOCALfreopen("test.txt", "r", stdin);// freopen("out.txt", "w", stdout);#endif // LOCALios::sync_with_stdio(false);scanf("%d", &T);while(T--) {ans = 0;memset(vis, 0, sizeof(vis));scanf("%d", &n);for(int i = 1; i <= n; ++i) {scanf("%d", &node[i].day);}for(int i = 1; i <= n; ++i) {scanf("%d", &node[i].score);}sort(node + 1, node + n + 1);for(int i = 1; i <= n; ++i) {int d = node[i].day;while(d > 0 && vis[d]) {--d;}if(d == 0) {ans += node[i].score;} else {vis[d] = true;}}printf("%d\n", ans);}return 0;}
在以如下方式标记的房间号中,要将桌子在任意两个房间之间移动
![]()
在从第 个房间移动到第 个房间的过程中,这个房间前面的走廊将会被占用,一个房间前的走廊只能放下一张桌子,每张桌子需要挪动 分钟,且桌子可以同时移动(在保证移动不出现冲突的情况下)。
这题按照房间的排列方式,可以将第 个房间对应到走廊的第 个位置,要保证走廊不会同时被两张桌子占用的情况下,移动时间最少,也就是求所有桌子占用重叠最多的位置。这样桌子的移动问题,就变成了求解桌子移动占用重叠最大的问题了,也就转化为了线段覆盖问题。
线段覆盖问题有许多变体与应用,这是其中一种:求给定的所有线段覆盖区间中,覆盖最多的次数,一般解法是先离散化每条线段的左右端点,然后在每一条线段的左端点标记一个 ,在每条线段的右端点往右一位标记,然后从头到尾加一遍,求加的的过程中取得的最大值,就是最大重叠数。
#include <iostream>#include <cstdio>#include <cstdlib>#include <cmath>#include <climits>#include <cstring>#include <string>#include <vector>#include <list>#include <queue>#include <stack>#include <map>#include <set>#include <bitset>#include <algorithm>#include <functional>#include <iomanip>using namespace std;#define LL long longconst int maxn = 300;int T, n, ans, l, r;int num[maxn];int main() {#ifdef LOCALfreopen("test.txt", "r", stdin);// freopen("out.txt", "w", stdout);#endif // LOCALios::sync_with_stdio(false);scanf("%d", &T);while(T--) {ans = 0;memset(num, 0, sizeof(num));scanf("%d", &n);for(int i = 0; i < n; ++i) {scanf("%d%d", &l, &r);if(l > r) {swap(l, r);}l = (l + 1) / 2;r = (r + 1) / 2;++num[l];--num[r + 1];}int tmp = 0;for(int i = 1; i < maxn; ++i) {tmp += num[i];ans = max(tmp, ans);}printf("%d\n", ans * 10);}return 0;}
一台机器要加工 根木棍,第 根木棍有一个长度 与重量 ,对于每根木棍的准备时间计算如下:
- 对于加工的第一根木棍,准备时间为 ;
- 如果已经加工过一根长为 ,重为 的木棍,接下来加工的长为 重为 的木棍的准备时间为 ;
问最少的准备时间。
先将木棍按照从小到大排序(无论以谁为关键字),然后将没有加工过的,长和重都大于当前木棍长、重的木棍进行加工,更新当前的长和重,最后输出重新加工的次数,已经加工过的木棍用 数组来标记。
#include <iostream>#include <cstdio>#include <cstdlib>#include <cmath>#include <climits>#include <cstring>#include <string>#include <vector>#include <list>#include <queue>#include <stack>#include <map>#include <set>#include <bitset>#include <algorithm>#include <functional>#include <iomanip>using namespace std;#define LL long longconst int maxn = 5000 + 100;struct Node {int l, w;};bool operator<(const Node &a, const Node &b) {if(a.l == b.l) {return a.w < b.w;}return a.l < b.l;}int T, n, ans;Node node[maxn];bool vis[maxn];int pull(int Index) {int ret = 0;int l = node[Index].l;int w = node[Index].w;for(int i = Index; i < n; ++i) {if(!vis[i] && node[i].l >= l && node[i].w >= w) {vis[i] = true;l = node[i].l;w = node[i].w;++ret;}}return ret;}int main() {#ifdef LOCALfreopen("test.txt", "r", stdin);// freopen("out.txt", "w", stdout);#endif // LOCALios::sync_with_stdio(false);scanf("%d", &T);while(T--) {ans = 0;memset(vis, 0, sizeof(vis));scanf("%d", &n);for(int i = 0; i < n; ++i) {scanf("%d%d", &node[i].l, &node[i].w);}sort(node, node + n);int cnt = n;while(cnt != 0) {for(int i = 0; i < n; ++i) {if(!vis[i]) {++ans;cnt -= pull(i);break;}}}printf("%d\n", ans);}return 0;}
从一个无前导零的 位整数中删除 位数字,使得最后得到的数字最小,若最后的数字存在前导零,则输出忽略这些前导零。
只按照数位从大到小删除显然是不对的,例如对于数字 只删除一位数,删除 得到的值为 ,删除 得到的值为 ,这种方法就行不通。从这个例子可以看出,应该从高位往底位删除每一个 的数字,删除 次就可以得到最后的答案,注意如果 的所有数位是非递减的,则要删掉最后一个数字,如果数位为 ,则不应该被删除。
这里的删除可以用 中的链表模拟,或者用 数组来标记是否被删除。
#include <iostream>#include <cstdio>#include <cstdlib>#include <cmath>#include <climits>#include <cstring>#include <string>#include <vector>#include <list>#include <queue>#include <stack>#include <map>#include <set>#include <bitset>#include <algorithm>#include <functional>#include <iomanip>using namespace std;#define LL long longconst int maxn = 1000 + 100;int m;char str[maxn];bool flag[maxn];int Find() {for(int i = 0; str[i]; ++i) {if(str[i] != '0' && !flag[i]) {for(int j = i + 1; str[j - 1]; ++j) {if(!flag[j]) {if(str[i] > str[j]) {return i;}break;}}}}return 0;}int main() {#ifdef LOCALfreopen("test.txt", "r", stdin);// freopen("out.txt", "w", stdout);#endif // LOCALios::sync_with_stdio(false);while(scanf("%s%d", str, &m) != EOF) {memset(flag, 0, sizeof(flag));for(int i = 0; i < m; ++i) {int Index = Find();flag[Index] = true;}int head = -1;for(int i = 0; str[i]; ++i) {if(str[i] != '0' && !flag[i]) {head = i;break;}}if(head == -1) {printf("0\n");} else {for(int i = head; str[i]; ++i) {if(!flag[i]) {printf("%c", str[i]);}}printf("\n");}}return 0;}
和 各有 张卡片, 想要让自己有最多的卡片能够覆盖 的卡片,一张卡片能够覆盖另一张卡片的条件是这张卡片的长 和宽 都不小于另一张卡片的长 和宽 ,且长宽不能交换。总共有 组数据。
这个问题的一般解法就是将两个数组从大到小(任选一个作为关键字)排序,对于每个 ,先将所有满足 对应的 值放入 中计数,然后从 中找到大于等于 的最小的数字,删掉这个数字,求能够删掉的最多的数字个数。
#include <iostream>#include <cstdio>#include <cstdlib>#include <cmath>#include <climits>#include <cstring>#include <string>#include <vector>#include <list>#include <queue>#include <stack>#include <map>#include <set>#include <bitset>#include <algorithm>#include <functional>#include <iomanip>using namespace std;#define LL long longconst int maxn = 100000 + 100;struct Node {int x, y;};bool operator<(const Node &a, const Node &b) {if(a.x == b.x) {return a.y > b.y;}return a.x > b.x;}int T, n, ans;Node node[maxn], mode[maxn];map<int, int> mp;map<int, int>::iterator it;int main() {#ifdef LOCALfreopen("test.txt", "r", stdin);// freopen("out.txt", "w", stdout);#endif // LOCALios::sync_with_stdio(false);scanf("%d", &T);while(T--) {scanf("%d", &n);ans = 0;mp.clear();for(int i = 0; i < n; ++i) {scanf("%d%d", &node[i].x, &node[i].y);}for(int i = 0; i < n; ++i) {scanf("%d%d", &mode[i].x, &mode[i].y);}sort(node, node + n);sort(mode, mode + n);int Index = 0;for(int i = 0; i < n; ++i) {while(Index < n && node[Index].x >= mode[i].x) {++mp[node[Index].y];++Index;}it = mp.lower_bound(mode[i].y);if(it != mp.end()) {--it->second;if(it->second == 0) {mp.erase(it);}++ans;}}printf("%d\n", ans);}return 0;}
与上一题相同,只是在“覆盖卡片数量最多”的条件上加上一条:每完成一个产品,可以获得 ,如果有多解,则输出能够获得的最多的价值。
基本思路与上题一样,只是对于附加条件,由于 且 ,所以无论 如何增加,都不如一个 的增量,因此想要价格最高,则必须将 作为主关键字, 作为第二关键字进行排序。
#include <iostream>#include <cstdio>#include <cstdlib>#include <cmath>#include <climits>#include <cstring>#include <string>#include <vector>#include <list>#include <queue>#include <stack>#include <map>#include <set>#include <bitset>#include <algorithm>#include <functional>#include <iomanip>using namespace std;#define LL long longconst int maxn = 100000 + 100;struct Node {int x, y;};bool operator<(const Node &a, const Node &b) {if(a.x == b.x) {return a.y > b.y;}return a.x > b.x;}int n, m, ans;LL price;Node node[maxn], mode[maxn];map<int, int> mp;map<int, int>::iterator it;int main() {#ifdef LOCALfreopen("test.txt", "r", stdin);// freopen("out.txt", "w", stdout);#endif // LOCALios::sync_with_stdio(false);while(scanf("%d%d", &n, &m) != EOF) {ans = 0;price = 0;mp.clear();for(int i = 0; i < n; ++i) {scanf("%d%d", &node[i].x, &node[i].y);}for(int i = 0; i < m; ++i) {scanf("%d%d", &mode[i].x, &mode[i].y);}sort(node, node + n);sort(mode, mode + m);int Index = 0;for(int i = 0; i < m; ++i) {while(Index < n && node[Index].x >= mode[i].x) {++mp[node[Index].y];++Index;}it = mp.lower_bound(mode[i].y);if(it != mp.end()) {--it->second;if(it->second == 0) {mp.erase(it);}++ans;price += mode[i].x * 500 + mode[i].y * 2;}}printf("%d %lld\n", ans, price);}return 0;}