CodeForces 729C Road to Cinema 二分

这道题我自己的思路是找到一个能够在要求时间内到达的最小油量,然后将车辆按照价格从小到大排序,第一个油箱大于最小油量的车子就是我们的选择。

怎么求最小油量呢?采用贪心的算法。

首先我们要解一个方程组

假设在这s米内,加速的距离是x, 平速的距离是y,很容易得出

x + y = s

x + 2 * y = t

然后得出x = 2 * s - t。现在我们要把这段加速距离分配下去,也就是我们在哪一段加速,在哪一段减速呢? 已知加油站把道路分成了若干段,我们先把这些段按照距离从小到大排列。现在我们思考:要想邮箱的容量尽可能的小,我们肯定希望把加速过程放在距离小的段上,如果放在距离大的段上,首先距离是最大的,油量耗的肯定要多,我们又把加速过程分配在了这里,那么耗的油量不是更多吗?

来解析一下第一个样例。

x = 2 * s - t = 2 * 8 - 10 = 6。即加速里程是6。

加油站把路程分成两段,3和5。

第一段全是加速,耗油量是6

第二段3里加速,2里平速,耗油量是8。

也就是能够在标准时间内到达的最小油量是max(6,8)=8。通过比较,很容易得出答案10。

这道题比赛的时候没想出来,后来队友补题,用上述方法a掉了。然后我也去补,同样的方法我居然wa了,最后得出结论,改数据了。

到底是改了哪一组数据让我们这个看起来对的算法wa了呢。

其实就是这种情况。

假如现在加油站把道路分成两段,5,5。我们算出的加速里程是2

按照我们先前的算法把2全部分配给第一个5,这样算出的最小耗油量就是7。但是还有更好的分配方案,就是每个5都分配一个1。我们的算法错在了,如果最小的段数有多个的话,就要把加速里程平均分给他们。咳咳,这样写起来有点小麻烦,而且我还不确定到底分给每一段的能不能是分数,如果不能是分数,相等的最小段中可能有一些分的是0。想了想实在是有点炸,还是搜搜题解,看看大神们是怎么做的把。不搜不知道一搜吓一跳,他们居然用的是二分来找出最小油量,实在让我叹为观止。

他们用的思路是这样的:对于每一个油量v,我们可以判断出能不能在t时刻内到达,怎么判断呢?同样是解方程

对于加油站分成的一小段路程l,我们设加速里程是x,平速里程是y,可以得出

x + y = L

x * 2 + y <= v

得出x <= v - s,要想时间最短,加速距离肯定最大,也就是x = v - l。求得这一小段距离的最短时间是x + 2 * y = 3 * L - v。

当油箱容量为v时,整段路程的最短时间就是每一段的3 * l - v加起来的总和,如果最短时间 <= t,这个容量就是可性的,我们在小于这个容量的范围内继续查找,否则就在大于容量的范围内查找。这不就是二分吗?

此外还要注意:v < L时,无论如何油量都不够走完这段路程,v > 2 * L时,这一段路程可以一直加速,也就是时间是L。也就是说我们上面算出来的这一段的最短时间3 * L - v是当L <= v <= 2 * L时的取值。

此外还要注意,二分很容易写错,千万小心。至于二分怎么写呢?有什么通用的模板吗,请看我的另一篇博客:二分总结http://blog.csdn.net/ecnu_lzj/article/details/52893977

代码如下:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define lson l, m, k << 1
#define rson m, r, k << 1 | 1

using namespace std;

typedef long long int LL;
const int INF = 0x3f3f3f3f;
const int maxn = 200005;
struct data{
    int p, f;
    bool operator < (const data &b) const{
        return p < b.p;
    }
};
data car[maxn];
int d[maxn], fuel[maxn];
int n, k, s, t;

bool judge(int v){
    int res = 0;
    for (int i = 0; i <= k; i++){
        int L = d[i];
        if (v > 2 * L) res += L;
        else if (v < L) return false;
        else res += 3 * L - v;
    }
    return res <= t;
}

LL Binary_search_minv(){
    LL left = 1, right = INT_MAX, mid;
    while (left <= right){
        mid = (left + right) >> 1;
        if (judge(mid))
            right = mid - 1;
        else left = mid + 1;
    }
    return left;
}

int main()
{
    //freopen("1.txt", "r", stdin);
    scanf("%d%d%d%d", &n, &k, &s, &t);
    for (int i = 0; i < n; i++)
        scanf("%d%d", &car[i].p, &car[i].f);
    sort(car, car + n);
    for (int i = 0; i < k; i++)
        scanf("%d", &fuel[i]);
    sort(fuel, fuel + k);
    for (int i = 1; i < k; i++)
        d[i] = fuel[i] - fuel[i - 1];
    d[0] = fuel[0];
    d[k] = s - fuel[k - 1];

    LL minv = Binary_search_minv();//二分找出最小的油量

    int ans = 0;
    for (int i = 0; i < n; i++){
        if (car[i].f >= minv){
            ans = car[i].p;
            break;
        }
    }
    if (minv == INT_MAX) printf("-1\n");
    else if (!ans) printf("-1\n");
    else printf("%d\n", ans);
    return 0;
}




你可能感兴趣的:(ACM-(技巧)二分答案)