算法题——贪心

贪心:追求眼前利益最大化

例题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;
    }
}

  

posted @ 2018-08-25 22:49  寻觅beyond  阅读(293)  评论(0)    收藏  举报
返回顶部