组合问题中的贪心法:
0-1背包问题:
背包问题(按照价值密度贪心准则,可以达到最优解):
活动安排问题:
在一个会场中,安排一批活动,活动的时间可能重复,要求计算出最多能够安排的活动数。
输入:第一行为活动数n。后面则有n行,每一行为活动开始时间和结束时间。
输出:最大活动安排数。
算法分析:
先把活动按照时间从大到小的顺序,进行排序。然后就可以用贪心思想来进行选择。
代码:
#include <iostream> #include <vector> #include <algorithm> using namespace std; typedef struct node { int start, end; }node; int n; vector<node> v; bool Compare(node n1, node n2) { return n1.end < n2.end; } void Setup() { cin >> n; node t; for (int i = 0; i < n; i++) { cin >> t.start >> t.end; v.push_back(t); } sort(v.begin(), v.end(), Compare); } int Select() { int maxCount = 1; int currectAct = 0; for (int i = 1; i < n - 1; i++) //直接从第二个开始 { if (v[i].start >= v[currectAct].end) { maxCount++; currectAct = i; //在当前基础上,继续找下一个 } } return maxCount; } int main() { Setup(); cout << Select() << endl; return 0; } /*测试数据: 输入: 5 1 23 12 28 25 35 27 80 36 50 输出: 3 */
图问题中的贪心法:
其他问题:
汽车加油问题:
汽车加满油之后,能够行驶n公里。沿途有k个加油站。
输入:第一行为n和k。第二行有k+1个数,是加油站之间的距离,第0个和最后一个加油站是起点和终点。
输出:最少加油次数。如果无法到达终点,则输出"No Solution"(如果汽车到达下一个加油站的距离大于n,那么汽车将会在到达加油站之前将油耗尽)。
代码:
#include <iostream> using namespace std; int n, k; int *station; void Setup() { cin >> n >> k; station = new int[k + 1]; for (int i = 0; i <= k; i++) { cin >> station[i]; } } int greedy() { int count = 0; for (int i = 0; i <= k; i++) { if (station[i] > n) { cout << "No Solution" << endl; return -1; } } int s = 0; for (int i = 0; i <= k; i++) { s += station[i]; //s代表一次性能走多远的距离 if (s > n) { count++; //如果加了这一次,而走不下去,那么需要加一次油;另一方面,由于这里的程序是,走过一个加油站而不需要加油的, //所以直接重置为到这一个加油站的距离,再加上下一次的距离判断能否继续前进 s = station[i]; } } return count; } int main() { Setup(); cout << greedy() << endl; } /*测试数据: 输入: 7 7 1 2 3 4 5 1 6 6 输出: 4 */
删数问题:
给定两个正整数Number和Count,按照数字Number原来的顺序,删除Count个任意数字后,得到一个新的正整数。请求出得到的最小数字。
输入:按顺序输入Number和Count。
输出:输出最小数字。
代码:
#include <iostream> #include <string> using namespace std; unsigned int Count = 0; string sourceNumber; void Setup() { cin >> sourceNumber >> Count; if (Count > sourceNumber.length()) { cout << "输入无效" << endl; } } string Greedy() { unsigned int deleteCount = 0, i = 0, accNum = 0; while (deleteCount < Count) { if (sourceNumber[accNum] < sourceNumber[accNum + 1]) //处于递增情况 { accNum++; if (accNum == sourceNumber.length() - 1) //如果一直递增 { sourceNumber.substr(0, Count - 1); } } else //出现递减的情况 { sourceNumber.erase(accNum, 1); accNum = 0; deleteCount++; } } return sourceNumber; } int main() { Setup(); cout << Greedy() << endl; return 0; } /*测试数据: 输入: 123456 4 输出: 12 */ /*测试数据: 输入: 178543 4 输出: 13 */
数列极差问题:
给定一个数列,每次擦去任意两个数a、b,然后再向数列中加入一个数a*b+1,重复上述操作,直到数列只剩一个数。从这些数中,选出最大和最小的数:Max、Min,则将数列极差定义为M=Max-Min。
算法分析:
例如一个只有三个数的数列{a,b,c},假设a<b<c,则可证明上述操作得到的Max=(a*b+1)*c+1,Min=(b*c+1)*a+1。所以对于一般数列来说,
求Max一定要重复地从最小的两个数来操作。而Min相反,要重复地从最大的两个数来操作。一定要记住两次操作要分开完成,不能互相干扰,否则不能得到结果。计算过程如下:
输入:第一行为数列长度,第二行为数列的值。
输出:第一行为M的位数,第二行为M的值。
代码:
#include <iostream> #include <vector> #include <algorithm> using namespace std; int n; vector<int> X, XForMin; void Setup() { cin >> n; for (int i = 0; i < n; i++) { int temp; cin >> temp; X.push_back(temp); XForMin.push_back(temp); } } bool CompareForMin(int n1, int n2) { return n1 > n2; } int GreedyForMax() { while (X.size() != 1) { sort(X.begin(), X.end()); int a = X[0], b = X[1]; X.erase(X.begin(), X.begin() + 2); X.push_back(a * b + 1); } return X[0]; } int GreedyForMin() { while (XForMin.size() != 1) { sort(XForMin.begin(), XForMin.end(), CompareForMin); int a = XForMin[0], b = XForMin[1]; XForMin.erase(XForMin.begin(), XForMin.begin() + 2); XForMin.push_back(a * b + 1); } return XForMin[0]; } int main() { Setup(); int M = GreedyForMax() - GreedyForMin(); int bitsM = 0; for (int temp = M; temp != 0; bitsM++) { temp = temp / 10; } cout << bitsM << endl << M << endl; return 0; } /*测试数据: 输入: 6 8 6 5 7 9 1 输出: 4 3672 */
旅行规划问题:
从A地到B地旅行的距离为d0,汽车油箱容量为c,每升油可以行驶e公里。出发点A地的加油站油价为p。旅途中有n个加油站,每个加油站到A地的距离为di,油价为pi。求旅行的最低花费。
输入:第一行为d0、c、e、p以及n,第二行为di、pi。
输出:旅行的最低花费,要求精确到小数点后2位。如果无法抵达终点B地,则输出"No Solution"。
算法分析:
加油方案可以为(x0, x1, ... , xn , xn),其中x0为A地的加油站,x1-xn为途中的n个加油站,xi={0,1},0不加,1加。那么目标值为minΣi=0n(pi * xi)。
首先必须要明白一个问题:到哪一站才加油,每一次加油加多少?——直接选择价格最低的贪心准则,对于第i个加油站,查找汽车最大行驶距离内的、油价最低的(不与此站相比)一个站;重复这个过程,直到终点B地。
代码: