zoukankan      html  css  js  c++  java
  • CF580B Kefa and Company 题解 双指针/二分

    题目链接:http://codeforces.com/problemset/problem/580/B

    题目描述

    聪聪最近领到了工资所以他决定去五星级大饭店大吃一顿,但是一个人吃不爽,他需要朋友的陪伴。
    聪聪有 (n) 个朋友,如果聪聪邀请这 (n) 个朋友中的任何一个人去吃饭,他们都会有时间陪伴聪聪去吃饭。
    聪聪的每个朋友都有有两个参数,对于第 (i) 个朋友,它的这两个参数表示如下:

    1. 富裕指数(m_i):这个参数表示第 (i) 个朋友多有钱,富裕指数越高表示这个朋友越有钱;
    2. 友情因子(s_i):这个参数表示如果第 (i) 个朋友陪伴聪聪一起去吃饭,聪聪能够提升的幸福值。

    聪聪想要他的幸福值提升地尽可能地大。
    但是考虑到他的朋友的富裕指数不同,所以如果他的朋友之间贫富差距过大的话就会出现“贫穷鄙视”。
    如果对于两个来吃饭的朋友 (i)(j) ,他们的富裕指数之差 (m_i - m_j gt d) ,就会出现“贫穷鄙视”,这会让朋友 (j) 觉得自己很穷而自卑。
    聪聪想在不出现“贫穷鄙视”的情况下获得最多的幸福值。
    他能够获得的幸福值就是所有来吃饭的朋友的友情因子之和。
    请帮他计算一下他能够获得的最大幸福值是多少!

    输入格式

    输入的第一行包含两个正数 (n)(d)(1 le n le 10^5)(1 le d le 10^9) ),分别表示聪聪朋友的个数和富裕指数的边界值。
    接下来 (n) 行用于描述聪聪的朋友,第 (i+1) 行包含两个参数 (m_i)(s_i)(0 le m_i,s_i le 10^9)),对应第 (i) 个朋友的富裕指数和友情因子。

    输出格式

    输出聪聪所能够获得的最大的幸福值。

    问题分析

    首先我们要考虑一下给我这道题我们怎么去解决。
    对于这道题,我们可以先给 (n) 个朋友按照 (m_i) 值从小到大排个序。
    排完序之后这道题目就简化成了:我们要找两个坐标 (i)(j) ,使得 (m_j - m_i < d) 并且 (sum(s_i cdots s_j)) 最大。(这里 (sum(s_i cdots s_j)) 表示 第 (i)(i+1)、……、(j) 个朋友的友情因子之和 )
    然后到了这里老师就想到了两种方法来解决这个问题。
    第一种方法是采用双指针的方式来解决这个问题,时间复杂度是 (O(n))
    实现思路是:一开始设一个变量 (j = 0) , 然后从 (0)(n-1) 遍历 (i) ,对于每一个 (i) ,如果 (m_i-m_j ge d) ,就执行 (j ++) 。然后从 (j)(i) 这个范围的朋友的友情因子之和就是答案的一个候选值。
    而对于 第 (j) 到 第 (i) 个好友的友情因子之和,我们可以在一开始用一个 (sum[]) 数组初始化一下( (sum[i]=sum[i-1]+s[i]) ),那么我们就可以以 (O(1)) 的时间复杂度求得从 (j)(i) 这个范围的朋友的友情因子之和 为 (sum[i] - sum[j-1])
    实现代码如下:

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn = 100010;
    int n;
    long long d, sum[maxn];
    
    struct Node {
        long long m, s;
    } a[maxn];
    
    bool cmp(Node a, Node b) {
        return a.m < b.m;
    }
    
    int main() {
        cin >> n >> d;
        for (int i = 0; i < n; i ++) cin >> a[i].m >> a[i].s;
        sort(a, a+n, cmp);
        for (int i = 0; i < n; i ++) sum[i] = a[i].s + ( i ? sum[i-1] : 0 );
        int j = 0;
        long long ans = 0LL;
        for (int i = 0; i < n; i ++) {
            while (a[i].m - a[j].m >= d) j ++;
            ans = max(ans, sum[i] - ( j ? sum[j-1] : 0 ) );
        }
        cout << ans << endl;
        return 0;
    }
    

    第二种方法是使用二分。
    因为聪聪的 (n) 个朋友按照 (m_i) 值从小到大排了序,所以 (m_i) 具有单调性。
    对于 第 (i) 个朋友,我们可以对区间 ([i, n]) 范围内的朋友进行二分查找,找到最大的那个满足 (mj-mi lt d)(j) ,这个时候 (sum[j] - sum[i-1]) 就是我们的一个候选答案。
    实现代码如下:

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn = 100010;
    int n;
    long long d, sum[maxn];
    
    struct Node {
        long long m, s;
    } a[maxn];
    
    bool cmp(Node a, Node b) {
        return a.m < b.m;
    }
    
    int main() {
        cin >> n >> d;
        for (int i = 0; i < n; i ++) cin >> a[i].m >> a[i].s;
        sort(a, a+n, cmp);
        for (int i = 0; i < n; i ++) sum[i] = a[i].s + ( i ? sum[i-1] : 0 );
        long long ans = 0LL;
        for (int i = 0; i < n; i ++) {
            int L = i, R = n-1, res;
            while (L <= R) {
                int mid = (L + R) / 2;
                if (a[mid].m - a[i].m < d) {
                    res = mid;
                    L = mid + 1;
                } else {
                    R = mid - 1;
                }
            }
            ans = max(ans, sum[res] - (i ? sum[i-1] : 0));
        }
        cout << ans << endl;
        return 0;
    }
    
  • 相关阅读:
    文件的打开和保存
    eclipse快捷键汇总
    FileNameExtensionFilter文件过滤
    java中文件保存、打开文件对话框
    FileInputStream(字节流)与fileReader(字符流) 的区别
    Java文本编辑器中遇到的问题详解
    前端基础 之 BOM和DOM
    前端基础 之JS
    前端基础 之 CSS
    前端基础之 HTML
  • 原文地址:https://www.cnblogs.com/quanjun/p/12206213.html
Copyright © 2011-2022 走看看