算法题——贪心
贪心:追求眼前利益最大化
例题1
加勒比海盗问题,海盗发现了很多宝藏,但是由于船的体积有限,所以只能运走总体积为C的宝藏,假设现在有n个宝藏,每一个宝藏的体积是Vi(0<i),宝藏不可以切割,请问,海盗船最多能运走多少个宝藏?
解析:将宝藏按照体积从小到大排序,然后在不超重的情况下,从小到大开始将宝藏放入海盗船。
注意:如果不在乎宝藏放入的顺序,那么,就可以只使用简单的一维数组保存体积即可。如果在乎宝藏的编号,那么可以使用结构题,保存编号和体积。
#include<iostream>
#include<algorithm>
using namespace std;
struct Treasure{
int id; //每个宝藏的编号
int V; //每个宝藏的体积
};
bool cmp(Treasure a, Treasure b) {
return a.V < b.V;
}
int main()
{
int n; //宝藏的总数量
int C; //海盗船的容量
while (cin >> C >> n) {
Treasure item[n];
for (int i = 0; i < n; i++) {
item[i].id = i + 1;
cin >> item[i].V;
}
//按照体积从小到大排序
sort(item, item + n, cmp);
int tot = 0; //放入的总体积
int cnt = 0; // 放入的宝藏总数量
for (int i = 0; i < n; i++) {
tot += item[i].V;
cnt++;
if (tot > C) { //放入宝藏后体积超了,于是将刚刚放入的宝藏取出来
tot -= item[i].V;
cnt--;
break;
}
}
cout << "tot=" << tot << endl << "cnt=" << cnt << endl;
}
return 0;
}
例题2:
加勒比海盗问题,海盗发现了很多宝藏,但是由于船的体积有限,所以只能运走总体积为C的宝藏,假设现在有n种宝藏,每一种宝藏有X个,每一个宝藏的体积是Vi(0<i),每一个宝藏不可以切割,请问,海盗船最多能运走多少个宝藏?
解析:仍旧将宝藏按照体积从小到大排序,然后在不超重的情况下,从小到大开始将宝藏放入海盗船
#include<iostream>
#include<algorithm>
using namespace std;
struct Treasure{
int X; //每种宝藏的数量
int V; //每种宝藏的单个体积
};
bool cmp(Treasure a, Treasure b) {
return a.V < b.V;
}
int main()
{
int n; //宝藏的总数量
int C; //海盗船的容量
while (cin >> C >> n) {
Treasure item[n];
for (int i = 0; i < n; i++) {
cin >> item[i].X >> item[i].V;
}
//按照体积从小到大排序
sort(item, item + n, cmp);
int tot = 0; //放入的总体积
int cnt = 0; // 放入的宝藏总数量
bool flag = true; //是否还能装宝藏
for (int i = 0; i < n; i++) {
if ( ! flag) break;
//对每种宝藏进行装载
for (int j = 0; j < item[i].X; j++) {
tot += item[i].V;
cnt++;
if (tot > C) { //放入宝藏后体积超了,于是将刚刚放入的宝藏取出来
tot -= item[i].V;
cnt--;
flag = false;
break;
}
}
}
cout << "tot=" << tot << endl << "cnt=" << cnt << endl;
}
return 0;
}
例题3:
加勒比海盗问题,海盗发现了很多宝藏,但是由于船的体积有限,所以只能运走总体积为C的宝藏,假设现在有n个宝藏,每一个宝藏的体积是Vi(0<i),每一个宝藏的价值为Wi,每一个宝藏不可以切割,请问,怎么保证海盗船运走的宝藏价值最高?
解析:此时,因为每个宝藏的体积和价值不同,那么可以按照性价比(W/V)从大到小排序,优先装载性价比高的宝藏。
#include<iostream>
#include<algorithm>
using namespace std;
struct Treasure{
double W; //每个宝藏的价值
double V; //每种宝藏的单个体积
double w_v; //性价比
};
bool cmp(Treasure a, Treasure b) {
return a.w_v > b.w_v;
}
int main()
{
int n; //宝藏的总数量
int C; //海盗船的容量
while (cin >> C >> n) {
Treasure item[n];
for (int i = 0; i < n; i++) {
cin >> item[i].V >> item[i].W;
item[i].w_v = (item[i].W * 1.0) / item[i].V; //性价比
}
//按照性价从大到小排序
sort(item, item + n, cmp);
int tot_v = 0; //放入的总体积
int cnt = 0; // 放入的宝藏总数量
int tot_w =0; //放入的总价值
bool flag = true; //是否还能装宝藏
for (int i = 0; i < n; i++) {
//对每种宝藏进行装载
tot_v += item[i].V;
tot_w += item[i].W;
cnt++;
if (tot_v > C) { //放入宝藏后体积超了,于是将刚刚放入的宝藏取出来
tot_v -= item[i].V;
tot_w -= item[i].W;
cnt--;
break;
}
}
cout << "tot_v=" << tot_v << endl;
cout << "tot_w=" << tot_w << endl;
cout << "cnt=" << cnt << endl;
}
return 0;
}
例题4:
加勒比海盗问题,海盗发现了很多宝藏,但是由于船的体积有限,所以只能运走总体积为C的宝藏,假设现在有n个宝藏,每一个宝藏的体积是Vi(0<i),每一个宝藏的价值为Wi,每一个宝藏可以切割,请问,怎么保证海盗船运走的宝藏价值最高?
解析:此时,因为每个宝藏的体积和价值不同,那么可以按照性价比(W/V)从大到小排序,优先装载性价比高的宝藏,最后一次,因为会超体积,那么只能装入那个宝藏的一部分。
#include<iostream>
#include<algorithm>
using namespace std;
struct Treasure{
double W; //每个宝藏的价值
double V; //每种宝藏的单个体积
double w_v; //性价比
};
bool cmp(Treasure a, Treasure b) {
return a.w_v > b.w_v;
}
int main()
{
int n; //宝藏的总数量
int C; //海盗船的容量
while (cin >> C >> n) {
Treasure item[n];
for (int i = 0; i < n; i++) {
cin >> item[i].V >> item[i].W;
item[i].w_v = (item[i].W * 1.0) / item[i].V; //性价比
}
//按照性价从大到小排序
sort(item, item + n, cmp);
int tot_v = 0; //放入的总体积
double tot_w =0; //放入的总价值
bool flag = true; //是否还能装宝藏
for (int i = 0; i < n; i++) {
//对每种宝藏进行装载
tot_v += item[i].V;
tot_w += item[i].W;
if (tot_v > C) { //放入宝藏后体积超了,于是将刚刚放入的宝藏取出来
tot_v -= item[i].V;
tot_w -= item[i].W;
//减掉体积超过的部分后,有剩余空间,但是不能装下一个完整的宝藏,但是可以装下这个宝藏的一部分
int last_v = C - tot_v;
tot_w += (last_v * item[i].w_v);
break;
}
}
cout << "tot_v=" << C << endl;
cout << "tot_w=" << tot_w << endl;
}
return 0;
}
例题5
时间表问题:只有一个会议室,但是现在有多个会议需要召开,已知每个会议的开始时间和结束时间【start)左闭右开区间,问最多可以安排多少个会议?要求会议不能有重叠。
解析:按照贪心的做法,只需要每一个会议都尽可能快地结束,那么就可以开始下一场会议,所以可以按照结束时间排序,优先安排先结束的会议。
#include<iostream>
#include<algorithm>
using namespace std;
struct Meeting{
int id;
int start;
int end;
};
bool cmp(Meeting a, Meeting b) {
return a.end < b.end;
}
int main()
{
int n; //有n个会议要安排
while (cin >> n) {
Meeting m[n];
for (int i = 0; i < n; i++) {
m[i].id = i + 1;
cin >> m[i].start >> m[i].end;
}
sort(m, m + n, cmp);
int last = m[0].end; //用来标记已经安排好的上一个会议的结束时间
int cnt = 1; // 可以安排的会议
for (int i = 1; i < n; i++) {
//如果当前会议的开始时间晚于上一个已安排的会议的结束时间,那么这个会议可以举行
if (m[i].start >= last) {
last = m[i].end;
cnt ++;
}
}
cout << "cnt=" << cnt << endl;
}
return 0;
}
问题6:删数问题
题目描述
给定一个正整数(<=255位),从中删去n位后,使得剩下的数字组成的新数最小。
输入格式第一行,要处理的正整数,第二行,一个整数n (n<255)
测试数据
15931 2
131
123456789 2
1234567
10008 2
0
10008 1
8
#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
using namespace std;
bool del[255];
int main(){
string s;
int n;
while (cin >> s >> n) {
memset(del, false, sizeof(del));
int length = s.length();
int cnt = 0;
for (int i = 0; cnt < n && i < length-1; i++) {
if (s[i] > s[i+1]) {
del[i] = true; //表示删除
cnt ++;
i++;
for (int j = i-2; cnt < n && j >= 0; j--) {
if( ! del[j] ){
if (s[j] <= s[i]) {
break;
} else {
del[j] = true;
cnt ++;
}
}
}
i--;
}
}
//针对123456789删除1位,结果是12345678
//针对10008删除2位后,是000,输出0;删1位后,输出8
bool all_zero = true;
for (int i = 0, t=0; t < length - n && i < length; i++) {
if (!del[i]) {
if (all_zero && s[i] != '0') { //出现非0的字母
all_zero = false;
}
if (!all_zero) {
cout << s[i];
}
t++;
}
}
if (all_zero) {
cout << 0;
}
cout << endl;
}
}
浙公网安备 33010602011771号