【CCF-CSP】202203-2. 出行计划
题目内容
【CCF-CSP】202203-2. 出行计划
思路分析
方法一:普通法(仅能获得70分)
思路
先将出行计划(进入场所时刻、需要C时刻内的核酸)依次存入数组中,然后根据所查询的做核酸的时刻,依次计算出各个出行计划是否在time_min和time_max的区间内(即核酸有效期),其中:
-
time_min为出核酸的时刻,等于做核酸的时刻+等待时长 -
time_max为核酸对于该场所的过期时刻,等于做核酸的时刻+等待时长+C
复杂度
下面分析复杂度。设出行计划、查询个数、等待时间为N,且$$N\in[0, 10^5]$$,则:
- 存储出行计划需要O(N)
- 遍历查询并依次比较要两次遍历,因此需要O(N^2)
所以整体的复杂度是O(N^2),当数据量很大的时候运行会超时,因此只能获得70分。
方法二:差分数组
思路
上层思想:建立一个数组A,存储所有时刻上有多少场所可以进入,这里的时刻指的是出核酸的时刻。
具体实现时,需要计算每个场所对应的可进入时间区间。如计划100时刻去某场所,而该场所要求24小时核酸,说明出核酸的时刻在100-24+1=77到100之间都是能够进入该场所的,那么就需要令A的[77,100]下标内的值都加一。
在实现A内的部分区间统一加一时,如果使用正常的遍历区间内的元素相加,时间复杂度会达到O(N),因此这里使用了差分数组。
差分数组
差分数组就是一个对应于a[N]的数组b[N],满足a[i] = b[1] + b[2] + … + b[i],设置这样的数组后,“部分区间统一加一”的这个操作复杂度就能由O(N)降为O(1)。例如,区间[3,6]统一加一,只需令b[3]+1,b[7]+1,这样a的对应区间就是全部加一的。
复杂度
依然设出行计划、查询个数、等待时间为N,且$$N\in[0, 10^5]$$,则:
- 遍历出行计划并维护数组A,O(N)
- 将差分数组整理为正常数组,O(K),其中$$K=4*10^5+1$$
- 遍历查询并直接输出结果,O(N)
所以整体复杂度是O(N),虽然K的值较大,但和庞大的O(N^2)比起来不值一提。
代码
方法一
#include<iostream>
#include<vector>
// 70分
using namespace std;
struct Plan {
int ptime;
int interval_require;
};
int main() {
int plan_num_n, query_num_m, wait_time_k;
cin >> plan_num_n >> query_num_m >> wait_time_k;
struct Plan plan;
vector<struct Plan> plans;
int queried_time, time_min, time_max;
int query_result;
for (int i=0; i<plan_num_n; i++) {
cin >> plan.ptime >> plan.interval_require;
plans.push_back(plan);
}
for (int i=0; i<query_num_m; i++) {
query_result = 0;
cin >> queried_time;
time_min = queried_time + wait_time_k;
for(struct Plan plan : plans) {
if (plan.ptime >= time_min && plan.ptime <= queried_time + \
wait_time_k + plan.interval_require - 1) {
query_result += 1;
}
}
cout << query_result << endl;
}
return 0;
}
方法二
#include<iostream>
#include<vector>
// 100分
// 满分题解:https://www.cfanz.cn/mobile/resource/detail/NGMVowMgZLjZJ
// 差分算法:https://www.jianshu.com/p/444096786e49
using namespace std;
const int TIME_NUM_N = 4e5 + 1;
int time2placenum[TIME_NUM_N]; // 差分数组,表示某时刻下可进入场所的个数
// 将数组的[left, right]之间的值都加上value
inline void increase_diff_array(int left, int right, int value) {
time2placenum[left] += value;
time2placenum[right + 1] -= value;
}
int main() {
int plan_num_n, query_num_m, wait_time_k;
cin >> plan_num_n >> query_num_m >> wait_time_k;
int place_time_t, interval_c;
int queried_time, time_min, time_max;
for (int i=0; i<plan_num_n; i++) {
cin >> place_time_t >> interval_c;
time_min = max(place_time_t - interval_c + 1, 1);
time_max = place_time_t;
increase_diff_array(time_min, time_max, 1);
}
for (int i=1; i<TIME_NUM_N; i++) {
time2placenum[i] += time2placenum[i-1];
}
for (int i=0; i<query_num_m; i++) {
cin >> queried_time;
cout << time2placenum[queried_time + wait_time_k] << endl;
}
return 0;
}
收获
以前一直觉得算法没卵用,今天这个例子是生活中确实存在的,用一些算法上的例子就能让程序的运行快上不少,证明算法还是有点卵用。
当然,这里假设时间是有限的,现实中可能做不到这样。