第六篇博客——贪心策略

简单贪心

鸡兔同笼

🤔如果已知动物的头和腿的数量,如果要求最多动物数量的时候就优先考虑脚少的🐔,如果要求最少数量的时候优先考虑脚多的🐰。

FatMouse’ s Trade

在遇到类似于购物问题时,优先购买性价比高的东西,等到买不下来了再选择买部分(如果可以的话,实际上这也是能够简单贪心的前提——能够部分购买)。

Senior’ s Gun

题目大意:一个人有若干支枪,面对若干个怪物。枪有攻击力,怪物有防御力,一把枪只能最多干掉一只怪物,杀死怪物后,这个人可以获得攻击力-防御力的奖赏,请问最多的奖赏是多少(不必干掉所有怪物)。

🤔既然一支枪只能干掉一只怪物,所以贪心策略是每次都用最强的枪杀死最弱的怪物,以便让每一枪都能获得当前情况下的金额最大的奖金。

练习:代理服务器

链接:https://www.nowcoder.com/questionTerminal/1284469ee94a4762848816a42281a9e0
来源:牛客网

使用代理服务器能够在一定程度上隐藏客户端信息,从而保护用户在互联网上的隐私。我们知道n个代理服务器的IP地址,现在要用它们去访问m个服务器。这 m 个服务器的 IP 地址和访问顺序也已经给出。系统在同一时刻只能使用一个代理服务器,并要求不能用代理服务器去访问和它 IP地址相同的服务器(不然客户端信息很有可能就会被泄露)。在这样的条件下,找到一种使用代理服务器的方案,使得代理服务器切换的次数尽可能得少

输入描述:

    每个测试数据包括 n + m + 2 行。
    第 1 行只包含一个整数 n,表示代理服务器的个数。
    第 2行至第n + 1行每行是一个字符串,表示代理服务器的 IP地址。这n个 IP地址两两不相同。
    第 n + 2 行只包含一个整数 m,表示要访问的服务器的个数。
    第 n + 3 行至第 n + m + 2 行每行是一个字符串,表示要访问的服务器的 IP 地址,按照访问的顺序给出。
    每个字符串都是合法的IP地址,形式为“xxx.yyy.zzz.www”,其中任何一部分均是0–255之间的整数。输入数据的任何一行都不包含空格字符。
     其中,1<=n<=1000,1<=m<=5000。
输出描述:
可能有多组测试数据,对于每组输入数据, 输出数据只有一行,包含一个整数s,表示按照要求访问服务器的过程中切换代理服务器的最少次数。第一次使用的代理服务器不计入切换次数中。若没有符合要求的安排方式,则输出-1。
#include <iostream>
#include <algorithm>
#include <cstring>
#include <valarray>
#include <vector>
#include <set>
#include <unordered_set>
using namespace std;

int main() {
    ios::sync_with_stdio(false);
    int n, m;
    while (cin >> n) {
      //n个代理服务器
        vector <string> proxies(n);
        for (int i = 0; i < n; i++) {
            cin >> proxies[i];
        }
        sort(proxies.begin(), proxies.end()); 
      // 后面二分搜索用到
        cin >> m;
      //m个需要访问的服务器
        vector <string> servers;
        string s;
        for (int i = 0; i < m; i++) {
            cin >> s;
            if (binary_search(proxies.begin(), proxies.end(), s)) { 
              // 如果需要请求的服务器非代理服务器,则一定不需要转换代理服务器,所以不考虑他们,只需考虑代理服务器需要被访问的情况
                servers.push_back(s);
            }
        }

        if (n == 1 && find(servers.begin(), servers.end(), proxies[0]) != servers.end()) {
          //这个函数的意思:在servers向量中寻找proxies[0]),如果找到那就是迭代器不指向servers.end(),那这种情况是不可能安排好的

          //<https://blog.csdn.net/e01528/article/details/89156964>
            cout << -1 << endl;
            continue;
        }

        // 模拟页表置换算法
        unordered_set <string> st;
        int ans = 0;
        for (const string& server : servers) {
          //C++ 11的语法 for(int a:b) 从数组b依次取出元素赋值给整型变量a,循环执行for中语句
            if (st.count(server) == 0) {
              //st中不能找到server就返回0,将server塞入st
                if (st.size() == n - 1) { // 如果页面已满,有请求页不在页表,则进行置换
                    ans++;
                    st.clear();
                }
                st.insert(server);
            }
        }
        cout << ans << endl;
    }
    return 0;
}

一个小tips:(dict.find(handle_str) != dict.end())比(dict.count(stmp) > 0)效率要高不少,不到一倍。

区间贪心

区间贪心,即有多个区间(比如时间),这些区间之间有的互相重叠,如何选择最多的互不重叠的区间即为区间贪心。

https://blog.csdn.net/Harington/article/details/87514015

今年暑假不AC

如何选择能够看到最多的电视节目?

  • 优先选择开始时间最早的电视节目,不能满足
  • 优先选择持续时间最短的节目,不能满足
  • 综合上述两点,可以知道,优先选择结束时间早的就可以了
//将电视节目按照
#include <iostream>
#include<algorithm>
using namespace std;
struct show{
  int st;
  int end;
};

const int MAXN=100;
show arr[MAXN];

bool compare(show s1,show s2){
  return s1.end<s2.end;
}

int main(){
  while(cin>>n){
    for(int i=0;i<n;i++){
      cin>>arr[i].st>>arr[i].end;
    }
    sort(arr,arr+n,compare);
    int t=0;
    int ans=0;        
    for(int i=0;i<n;i++){
      if(t<arr[i].st){
        ans++;
        t=arr[i].end;
      }
    }
    cout<<ans<<endl;
  }
  return 0;
}

Case of Fugitive

题目大意:n个岛屿,在一条线上,岛屿有起点和终点,有m座桥,问能不能用其中的n-1座桥把岛屿连起来

思路:可以先把n个岛屿的位置,预处理成n-1个所需桥的长度范围,然后按照长度范围的最小值升序排列,把桥的长度按照升序排列并以桥开始选择区间,在满足的条件下,区间的max中最小的优先。

练习:To fill or Not to Fill

#include <iostream>
#include <cstdio>
#include <algorithm>
/*
50 1300 12 8
6.00 1250
7.00 600
7.00 150
7.10 0
7.20 200
7.50 400
7.30 1000
6.85 300
50 1300 12 2
7.10 0
7.00 600

749.17
The maximum travel distance = 1200.00
*/
using namespace std;
/*
输入 : 
对于每种情况,第一行包含4个正数:Cmax(<=100),即油箱的最大容量;D(<=30000),
即杭州到目的地城市的距离;Davg(<=20),即汽车每单位汽油可行驶的平均距离;N(<=500),
即加油站总数。接下来是N行,每行包含一对非负数:Pi,煤气单价,Di(<=D),这个站到杭州的距离,i=1,…N。一行中的所有数字用空格隔开。

输出 :
对于每个测试用例,一行打印最便宜的价格,精确到小数点后2位。
假设开始时油箱是空的。如果无法到达目的地,请打印“最大行驶距离=X”,
其中X是车辆可以行驶的最大可能距离,精确到小数点后2位。 
*/
struct GasStation {
    double price;
    int distance;
};

bool ComparePrice(GasStation x, GasStation y) {
    return x.price < y.price;
}

int main() {
    int cmax, d, davg, n; // cmax : 箱的最大容量, d : 州到目的地城市的距离, davg : 车每单位汽油可行驶的平均距离, n : 加油站总数;
    while (cin >> cmax >> d >> davg >> n) {
        double currentprice = 0; // 当前油费 

        bool tag[d + 1]; // 记录当前有哪段道路是从加油站出发能走的 
        GasStation gasStation[n];

        for (int i = 1; i <= d; ++i) tag[i] = false;
        for (int i = 0; i < n; ++i) cin >> gasStation[i].price >> gasStation[i].distance;

        sort(gasStation, gasStation + n, ComparePrice); // 对油价按升序排 

        for (int i = 0; i < n; ++i) {  // 对tag[]进行记录, 并同时计算出 currentprice
            int currentdistance = 0; // 记录从这个加油站出发要用其油的距离 
            for (int j = gasStation[i].distance + 1; j <= gasStation[i].distance + cmax * davg; ++j) {
                if (tag[j] == false) { // 如果 tag[j]为假则可走 
                    tag[j] = true;
                    currentdistance++;
                }
                if (j == d || j == gasStation[i].distance + cmax * davg) { // 走到了尽头 
                    currentprice += gasStation[i].price  * currentdistance / (davg * 1.0);
                    break;
                }
            }
        }

        int fill = 1; // tag[]是否全为真的标志位
        double journey = 0;
        for (int i = 1; i <= d; ++i) {
            if (tag[i] == true) journey++;
            else {
                fill = 0;
                break;
            }
        }

        if (fill) printf("%.2f\n",currentprice);
        else printf("The maximum travel distance = %.2f\n", journey);    
    }
    return 0;
}