[机试]第七章 贪心策略


part 1 简单贪心

例题7.1 贪心策略

KY87 鸡兔同笼

#define _CRT_SECURE_NO_DEPRECATE
#include <iostream>

using namespace std;


int Least(int a) {
    
    
	int least = 0;
	while (a > 4) {
    
    
		a -= 4;
		least++;
	}

	if (a == 0) {
    
    
		return least;
	}
	else if (a == 2 || a == 4) {
    
    
		return least + 1;
	}
	else {
    
    
		return 0;
	}
}

int Max(int a) {
    
    
	int max = 0;
	while (a > 2) {
    
    
		a -= 2;
		max++;
	}
	if (a == 2) {
    
    
		return max + 1;
	}
	else if (a == 0) {
    
    
		return max;
	}
	else {
    
    
		return 0;
	}
}
int main() {
    
    
	int a;
	while (scanf("%d", &a) != EOF) {
    
    
		printf("%d %d\n", Least(a), Max(a));
	}
	return 0;
}
  • c++整数除法默认是下取整

例题7.2 FatMouse’ Trade

HDU1009

#define _CRT_SECURE_NO_DEPRECATE
#include <iostream>
#include <cstdio>
#include <algorithm>

using namespace std;

struct javaBean {
    
    
	double weight; // J[i]
	double cost; // F[i] food 换 bean
};

javaBean info[1001];

bool Compare(javaBean a, javaBean b) {
    
    
	return (a.weight / a.cost) > (b.weight / b.cost);
}

int main() {
    
    
	int m, n;
	while (scanf("%d %d", &m, &n) != EOF) {
    
    
		if (m == -1 && n == -1) {
    
    
			break;
		}
		for (int i = 0; i < n; ++i) {
    
    
			scanf("%lf %lf", &info[i].weight, &info[i].cost);
		}
		sort(info, info + n , Compare);
		double myjavaBean = 0;
		for (int i = 0; i < n; ++i) {
    
    
			if (m >= info[i].cost) {
    
    
				myjavaBean += info[i].weight;
				m -= info[i].cost;
			}
			else {
    
    
				myjavaBean += info[i].weight * (m / info[i].cost);
				break; // 不要忘了break!!!!
			}
		}
		printf("%.3f\n", myjavaBean);
	}
	return 0;
}
  • Vector的排序(用vector能存的用数组也可以哈)
    sort(v.begin(), v.end(),less<int>());//升序
    sort(v.begin(), v.end(),greater<int>());//降序
    写Compare函数
  • 需要输出double类型时,过程中的计算一定要注意精度转换问题(直接全用double存最好!!)
  • while循环判断边界条件不方便时,用if循环,到边界条件时一定要加break跳出循环!!
  • 数学计算问题,需要算出每一单位cost能够换取多少weight,weight / cost,不要颠倒
  • 贪心策略:总是选择当前状态下的最优的策略。(只顾眼前,不顾整体)能得到局部最优解,往往能收获全局最优,但不能保证全局最优(无后效性的最优化问题,贪心策略可以得到全局最优,某个状态以前的过程不会影响以后的状态)

例题7.3 Senior’s Gun

HDU - 5281

#define _CRT_SECURE_NO_DEPRECATE
#include <iostream>
#include <cstdio>
#include <algorithm>

using namespace std;

long long attack_power[100000];
long long defensive_power[100000];

int main() {
    
    
	int t;
	scanf("%d", &t);
	while (t--) {
    
    
		int n, m;
		scanf("%d %d", &n, &m);

		for (int i = 0; i < n; ++i) {
    
    
			scanf("%lld", &attack_power[i]);
		}
		sort(attack_power, attack_power + n, greater<int>());
		
		for (int i = 0; i < m; ++i) {
    
    
			scanf("%lld", &defensive_power[i]);
		}
		sort(defensive_power, defensive_power + m);
		long long bonus = 0;
		for (int i = 0; i < n; ++i) {
    
    
			if (i >= m || attack_power[i] <= defensive_power[i]) {
    
    
				break;
			}
			bonus += attack_power[i] - defensive_power[i];
			
		}
		printf("%lld\n", bonus);
	}
	return 0;
}
  • 循环break条件:当火力<=防御力时或index>=m怪物数
  • 攻击力和防御力的差超过int(10次方)表示范围,用long long(19次方)

习题7.1 代理服务器

KY4 代理服务器

#define _CRT_SECURE_NO_DEPRECATE
#include <iostream>
#include <cstring>

using namespace std;

int main() {
    
    
	int n;
	while (cin >> n) {
    
    
		char proxy[n][16];
		for (int i = 0; i < n; ++i) {
    
    
			cin >> proxy[i];
		}
		
		int m = 0;
		cin >> m;
		char server[m][16];
		for (int i = 0; i <  m; ++i) {
    
    
			cin >> server[i];
		}

		int count = 0, index = 0, flag = 1;
		while (flag && index != m) {
    
    
			int max = 0;
			for (int i = 0; i < n; ++i) {
    
    
				int j = index;
				while (strcmp(proxy[i], server[j]) && j < m) {
    
    
					j++;
				}
				if (j - index > max) {
    
     // 选择此次迭代能够走的最远代理服务器
					max = j - index;
				}
			}
			if (max == 0) {
    
    
				flag = 0;
			}
			count++;
			index += max;
		}
		if (flag) {
    
    
			cout << count - 1 << endl; // 第一次不转换
		}
		else {
    
    
			cout << "-1" << endl;
		}
	}
	return 0;
}
  • strcmp()函数是根据ACSII码的值来比较两个字符串的;strcmp()函数首先将s1字符串的第一个字符值减去s2第一个字符,若差值为零则继续比较下去;若差值不为零,则返回差值。头文件#include <cstring>
  • 这类题目找到贪心策略是核心!!!不同题的贪心策略不同。本题的贪心策略是:每次选择能访问最远的ip地址进行访问,再从断点开始换下一个能访问最远的ip

part 2 区间贪心

例题7.4 今年暑假不AC

HDU_今年暑假不AC

#define _CRT_SECURE_NO_DEPRECATE
#include <iostream>
#include <cstdio>
#include <algorithm>

using namespace std;

struct program {
    
    
	int time_s;
	int time_e;
};

program programs[101];

bool Compare(program a, program b) {
    
    
	return a.time_e < b.time_e;
}

int main() {
    
    
	int n;
	while (scanf("%d", &n) != EOF) {
    
    
		if (n == 0) {
    
    
			break;
		}
		for (int i = 0; i < n; ++i) {
    
    
			scanf("%d %d", &programs[i].time_s, &programs[i].time_e);
		}
		sort(programs, programs + n, Compare);
		int count = 0;
		int current_time = 0;
		for (int i = 0; i < n; ++i) {
    
    
			if (current_time <= programs[i].time_s) {
    
    
				count++;
				current_time = programs[i].time_e;
			}
		}
		printf("%d\n", count);
	}

	return 0;
}
  • 区间贪心:存在多个不同区间,有些区间可能有重叠,选取最多的两两互不相交的区间。
  • 贪心策略:剩余观看时间最大化 => 每次选择结束时间最早的节目;而后判断当前时间和当前最优节目开始时间是否冲突,更新当前时间为最优节目的结束时间。

我很疑惑,为什么这个边界判断就不行?
在这里插入图片描述

例题7.5 Case of Fugitive

CF555B Case of Fugitive

#define _CRT_SECURE_NO_DEPRECATE
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue> 
#include <cstring>

using namespace std;

const int MAX = 200001;

struct island {
    
    
	long long left;
	long long right;
};

struct bridge {
    
    
	long long length;
	long long index;
};

struct gap {
    
    
	long long min;
	long long max;
	long long index;
	bool operator<(gap x) const {
    
     
		return max > x.max; 
	}
};

bool Compare_gap(gap a, gap b) {
    
     
	if (a.min == b.min) {
    
    
		return a.max < b.max;
	}
	else {
    
    
		return a.min < b.min;
	}
}


bool Compare_bridge(bridge a, bridge b) {
    
    
	return a.length < b.length;
}

island islands[MAX]; // n
bridge bridges[MAX]; // m
gap gaps[MAX]; // n - 1
long long answer[MAX];

bool Solve(int n, int m) {
    
    
	priority_queue<gap> myQueue;
	int position = 0;
	int number = 0;
	for (int i = 0; i < m; ++i) {
    
    
		while (!myQueue.empty() && myQueue.top().max < bridges[i].length) {
    
    
			myQueue.pop();
		}

		while (position < n - 1 && gaps[position].min <= bridges[i].length && gaps[position].max >= bridges[i].length) {
    
    
			myQueue.push(gaps[position]);
			position++;
		}

		if (!myQueue.empty()) {
    
    
			gap current = myQueue.top();
			myQueue.pop();
			answer[current.index] = bridges[i].index;
			number++;
		}
	}
	return number == n - 1;
}

int main() {
    
    
	int n, m;
	while (scanf("%d %d", &n, &m) != EOF) {
    
    
		
	/*	memset(islands, 0, sizeof(islands)); 
		memset(bridges, 0, sizeof(bridges));
		memset(gaps, 0, sizeof(gaps));
		memset(answer, 0, sizeof(answer));*/

		for (int i = 0; i < n; ++i) {
    
    
			scanf("%lld %lld", &islands[i].left, &islands[i].right);
		}

		for (int i = 0; i < m; ++i) {
    
    
			scanf("%lld", &bridges[i].length);
			bridges[i].index = i + 1;
		}

		for (int i = 0; i < n - 1; ++i) {
    
    
			gaps[i].min = islands[i + 1].left - islands[i].right;
			gaps[i].max = islands[i + 1].right - islands[i].left;
			gaps[i].index = i;
		}
		sort(gaps, gaps + n - 1, Compare_gap);
		sort(bridges, bridges + m, Compare_bridge);
		if (Solve(n, m)) {
    
    
			printf("Yes\n");
			for (int i = 0; i < n - 1; ++i) {
    
    
				printf("%lld ", answer[i]);
			}
		}
		else {
    
    
			printf("No\n");
		}
	}
	return 0;
}
  • leetcode,hduoj,洛谷,牛客,百炼oj,virtualjudge,浙大PAT&oj都是很棒的刷题网站噢~

    扫描二维码关注公众号,回复: 13543331 查看本文章
  • 题解:洛谷上有很多大神的解答,小白表示放眼望去就很难理解的感觉(我发现我总是把if当成while,或者把while想成if,然后就蒙圈了)
    在这里插入图片描述
    在这里插入图片描述

  • memset头文件#include <cstring>,将数字以单个字节逐个拷贝的方式放到指定的内存中去。初始化的内存,清零。 memset(structname,0,sizeof(structname));但这道题后续我们就输入数据覆盖这个数组了,其实也可以不清零。

  • 优先队列的头文件也是#include <queue>,声明格式为priority_queue<结构类型> 队列名;
    在优先队列中,元素被赋予优先级。当访问元素时,具有最高优先级的元素最先删除。优先队列具有最高级先出 (first in, largest out)的行为特征。默认是最大堆排序(即top()得到的是最大的元素)

    1. 重载小于号bool operator<(node x)const{ return elem>x.elem;} 升序,小的先出队列
      升序队列,小顶堆 priority_queue <int,vector,greater > q;
    2. 重载小于号bool operator<(node x)const{ return elem<x.elem;} 降序,大的先出队列
      降序队列,大顶堆 priority_queue <int,vector,less >q;
    3. 我们把Vector的排序拿过来做一个对比,sort(v.begin(), v.end(),less<int>());//升序sort(v.begin(), v.end(),greater<int>());//降序,发现这里的less<int>和我们平时重载sort比较函数时的思路一样,小于则数据结构元素是升序排列的。但在优先队列中,所表达的是降序队列,即大顶堆,大的元素先出队,出队后的数据结构元素是降序排列的噢!
  • 本题的核心贪心策略就是,以将所有的gap都堵上为目标,将最合适的桥覆盖在最合适的gap上。用短的桥覆盖小的gap。当然这个贪心策略也可以用点和区间来解释,大家可以查看洛谷上大神的讲解。

习题7.2 To Fill or Not to Fill

KY155 To Fill or Not to Fill

#define _CRT_SECURE_NO_DEPRECATE
#include <cstdio>
#include <algorithm>

using namespace std;

struct Station {
    
    
	double price;
	double distance;
};

Station station[510];

bool Compare(Station a, Station b) {
    
    
	return a.distance < b.distance;
}

int main() {
    
    
	double C_max, D, D_avg;
	int N;
	// C_max: max capacity of the tank
	// D: distance to destination city
	// D_avg: distance per unit gas
	// N: number of gas station
	scanf("%lf %lf %lf %d", &C_max, &D, &D_avg, &N);
	for (int n = 0; n < N; ++n) {
    
    
		scanf("%lf %lf", &station[n].price, &station[n].distance);
	}

	station[N].distance = D;
	station[N].price = 0.00;
	sort(station, station + N, Compare);

	double maxRun = C_max * D_avg;
	double currentgas = 0.00;
	double needgas = 0.00;
	double addgas = 0.00;
	double cost = 0.00;

	if (station[0].distance != 0) {
    
    
		printf("The maximum travel distance = 0.00\n");
		return 0;
	}

	int i = 0;
	while (i < N) {
    
    
		int k = 0;
		while (i + k <= N && station[i + k].distance - station[i].distance <= maxRun) {
    
    
			k++;
		}
		k--;
		if (k <= 0) {
    
    
			printf("The maximum travel distance = %.2f\n", station[i].distance + maxRun);
			return 0;
		}
		int j;
		for (j = i; j <= i + k; ++j) {
    
    
			if (station[j].price < station[i].price) {
    
    
				break;
			}
		}
		if (j <= i + k) {
    
    
			needgas = (station[j].distance - station[i].distance) / D_avg;
			if (currentgas >= needgas) {
    
    
				currentgas -= needgas;
			}
			else {
    
    
				addgas = needgas - currentgas;
				currentgas = 0.00;
				cost += addgas * station[i].price;
			}
		}
		else {
    
    
			j = i + 1;
			for (int tmp = i + 1; tmp <= i + k; ++tmp) {
    
    
				if (station[tmp].price < station[j].price) {
    
    
					j = tmp;
				}
			}
			needgas = (station[j].distance - station[i].distance) / D_avg;
			addgas = C_max - currentgas;
			currentgas = C_max - needgas;
			cost += station[i].price * addgas;
		}
		i = j;
	}
	printf("%.2f\n", cost);
	return 0;
}
  • 看了一些解答,和我思路比较一致的一个贪心策略:将加油站以距离排序,选择在C_max * D_avg距离内比当前价格便宜的最近的加油站为下一站,如果不存在,就选择在C_max plus D_avg距离内最便宜的加油站为下一站,并在本站把油加满
  • 特殊情况:第一个加油站station[0]距离不为零,终点设置为最后一个加油站以及下一个加油站无法到达的情况。

猜你喜欢

转载自blog.csdn.net/weixin_44145782/article/details/120004831