原题链接: AcWing 4114. 垃圾桶
题目来源:AcWing 夏季每日一题2023
一条很长的街道上有 N
个房子。
第一个房子在位置 1,第二个房子在位置 2,以此类推。
任意一对房子 i
和 j
之间的距离为 |i−j|
。
一些房子的位置处有垃圾桶。
每个房子的主人都要倒垃圾。
如果自己房子前面有垃圾桶,则无需移动,直接倒垃圾即可。
如果自己房子前面没有垃圾桶,则前往距离自己最近的垃圾桶处倒垃圾,如果这样的垃圾桶不唯一,则任意前往一个即可。
请计算,所有房子的主人倒垃圾需要行走的总距离之和。
输入格式
第一行包含整数 T
,表示共有 T
组测试数据。
每组数据第一行包含整数 N
。
第二行包含一个长度为 N
的 01 字符串,第 i 个字符如果为 1
,则表示第 i 个房屋门前有垃圾桶,如果为 0
,则表示第 i 个房屋门前没有垃圾桶。
输出格式
每组数据输出一个结果,每个结果占一行。
结果表示为 Case #x: y
,其中 x
为组别编号(从 1 开始),y
为所有房子的主人倒垃圾需要行走的总距离之和。
数据范围
- 1≤T≤100,
- 1≤N≤5×105,
- 字符串中至少包含一个 1。
输入样例:
2
3
111
6
100100
输出样例:
Case #1: 0
Case #2: 5
样例解释
对于 Case 1,每个房子门前都有垃圾桶,所以大家都不用移动。
对于 Case 2,第 1,4
个房子的门前有垃圾桶,这两家主人不用移动,第 2
个房子的主人去第 1
个房子处倒垃圾,第 3,5,6
个房子的主人去第 2
个房子处倒垃圾。
方法一:枚举
思路:
- 首先,n最大有5 * 105,假设极端情况,第一个一个是1,后面全是0,那么结果就是0+1+2+…+5 * 105,这是会爆int的,因此要用long long来存
- 经过判断,必须采用O(n)的算法才能过
- 开两个数组,l[i]存i左边最近的垃圾桶位置,r[i]存i右边最近的垃圾桶位置,则i的到垃圾桶的最近距离就是两个距离取min
C++ 代码:
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn = 500010, INF = 1e8;
int n;
char s[maxn];
int l[maxn], r[maxn];
int main(){
int T;
scanf("%d", &T);
for(int cases = 1; cases <= T; cases ++ ){ // 一共有t个询问
scanf("%d", &n);
scanf("%s", s);
// 从左到右预处理 l[i]表示i左边最近的垃圾桶的位置 t为当前最近的垃圾桶的位置
for(int i = 0, t = -INF; i < n; i ++ ){
if(s[i] == '1') t = i;
l[i] = t;
}
// 从右到左预处理 r[i]表示i右边最近的垃圾桶的位置 t为当前最近的垃圾桶的位置
for(int i = n - 1, t = INF; i >= 0; i -- ){
if(s[i] == '1') t = i;
r[i] = t;
}
// 遍历一次得到结果 距离最近的取左右两边距离的min
ll res = 0;
for(int i = 0; i < n; i ++ ){
res += min(i - l[i], r[i] - i);
}
printf("Case #%d: %lld\n", cases, res); // 注意这个lld
}
return 0;
}
复杂度分析:
- 时间复杂度:O(n),一共需要遍历数组3次,O(3n)也就是O(n)
- 空间复杂度:O(n),开了两个辅助数组