这题必须写下题解,写了好久找了好久的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代码
#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;
}
总结
算法题就是要多动手,多自己手写,会发现各种各样的问题,只有通过这些问题才能不断进步,不然只是纸上谈兵,空有理论。
算法题还是挺有意思的,要发现其中的乐趣,动力不就源源不断的来了嘛