洛谷-P1314 [NOIP2011 提高组] 聪明的质监员

这题必须写下题解,写了好久找了好久的bug,如果你也卡了很久找不到bug可以看看我的找bug的旅程。

题目

题目链接

读题

首先读题大家肯定没有问题,但是我不知道大家有没有注意到一个情况,就是题目给的样例中,矿物是按重量排序大小排序的,就是这一点把我坑惨了,还以为其他测试数据也是如此,就直接默认是有序的然后进行二分搜索发现,一个AC也没有。

样例

输入

5 3 15  
1 5 
2 5 
3 5 
4 5 
5 5 
1 5 
2 4 
3 3 

输出

10

思路

首先你可以读题发现,直接通过他给的思路模拟,就是一种办法,本着暴力也是一种解法的态度,写下了第一版代码,就是完全按着题目的介绍来模拟。第一版发现了题目完全可以离散化,就是搜索的时候解的空间是 符合W标准矿物的个数 ,搜索的时候就遍历这个个数就可以找到最佳答案
在这里插入图片描述

然后发现可以用二分搜索来优化搜索。
二分搜索的使用条件一定有一个是 搜索空间是有序的,这样才能使用二分搜索,所以我又另开了一个数组,来存储这个有序的矿物的信息。那为什么要重新开一个数组呢?不是纯纯浪费空间吗?不!当然不是,因为题目已经给矿物编好序号了,如果我打乱重新排序,就会导致后面根据区间来算y就会出错,这也是非常容易忽略的一点,在很多题目中也容易忽略这一点。
当时真崩溃
忽略那一点导致AC的还不如之前模拟的,然后才发现那个问题
离AC又进了一步
最后又突然发现这种因为题目有重叠区间的反复相加求和的操作,这就构成了使用前缀和数组的前提条件,然后使用前缀和可以只进行一次求前缀和数组的操作,再求区间的和就只用收尾相减就可以求出区间的和 大大节省了时间。
在这里插入图片描述
竟然还有一个错,然后仔细检查才发现 是二分搜索的循环判断条件写错了应该是 >=
最终可歌可泣终于AC了
在这里插入图片描述

附上AC代码

#include <bits/stdc++.h>
using namespace std;
struct stone
{
    long long w; // 重量
    long long v; // 价值
};
bool compare(struct stone s1, struct stone s2)
{
    return s1.w < s2.w;
}
int main()
{
    long long n, m, s;
    cin >> n >> m >> s;
    struct stone rock[200001];
    struct stone rock_list[200001];
    for (int i = 1; i <= n; i++)
    {
        cin >> rock[i].w >> rock[i].v;
        rock_list[i].w = rock[i].w;
        rock_list[i].v = rock[i].v;
    }
    long *l = new long[m], *r = new long[m];
    for (int i = 0; i < m; i++)
    {
        cin >> l[i] >> r[i];
    }
    sort(rock_list + 1, rock_list + n, compare);
    long mini = 0x3f3f3f3f3f3f3f3f;
    int left = 0, right = n, mid = (right + left) / 2;
    while (left <= right)
    {
        // 使用前缀和
        struct stone *rock_temp = new struct stone[n + 1];
        long *num = new long[n + 1];
        num[0] = 0;
        rock_temp[0].v = 0;
        rock_temp[0].w = 0;
        for (int i = 1; i <= n; i++)
        {
            if (rock[i].w >= rock_list[mid].w + 1)
            {
                rock_temp[i].v = rock[i].v + rock_temp[i - 1].v;
                num[i] = num[i - 1] + 1;
            }
            else
            {
                rock_temp[i].v = rock_temp[i - 1].v;
                num[i] = num[i - 1];
            }
        }
        long long y = 0;
        for (int i = 0; i < m; i++)
        {
            long l1 = l[i];
            long r1 = r[i];
            y += (num[r1] - num[l1 - 1]) * (rock_temp[r1].v - rock_temp[l1 - 1].v);
        }
        // 这是未使用前缀和的过程
        //  long long y = 0;
        //  for (int j = 0; j < m; j++)
        //  {
        //      long l1 = l[j];
        //      long r1 = r[j];
        //      long num = 0;
        //      long long sum = 0;
        //      for (int k = l1; k <= r1; k++)
        //      {
        //          if (rock[k].w >= rock_list[mid].w + 1)
        //          {
        //              num++;
        //              sum += rock[k].v;
        //          }
        //      }
        //      y += sum * num;
        //  }
        if (llabs(y - s) < mini)
        {
            mini = llabs(y - s);
        }
        if (y < s)
        {
            right = mid - 1;
        }
        else if (y == s)
        {
            break;
        }
        else if (y > s)
        {
            left = mid + 1;
        }
        mid = (left + right) / 2;
    }
    cout << mini;
}

总结

算法题就是要多动手,多自己手写,会发现各种各样的问题,只有通过这些问题才能不断进步,不然只是纸上谈兵,空有理论。
算法题还是挺有意思的,要发现其中的乐趣,动力不就源源不断的来了嘛

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值