题意
Vasiliy需要在接下来的天内进行
门功课的测验,对于第i门功课他需要
天来复习和一天来考试,并且他知道在这
天内每天可以进行哪一门功课的测验(或者没得考试)。在这
天内每一天他可以选择进行某门功课的测验,或者选择复习某门功课(只能选一个!)现在你要帮他算一下他能否在这
天内通过所有功课的测验,如果能的话输出通过所有测验的最小天数,否则输出
。
思路
首先考试肯定是能考就考,要考就要先复习!从第一门考试开始看起......秉持着能考就考的原则,第一门考试当然要考!第二门也要!第三门!......可以发现,如果我们要进行某几门测验,就要花费这几门测验所需的(复习+考试(一天))时间之和,而我们拥有的时间刚好就是这几门测验里最后一门测验的时间——时间之和,顺序遍历考试——前缀和!于是思路骤然明朗:
顺序遍历一遍所有考试,处理一下考试所需时间的前缀和,并与这几门测验里的最后一门测验的时间点进行比较,若考试时间大于测验时间则说明不行,反之则可以。
但是我们需要考完所有考试吗?首先已经通过的功课不需要再去考了,因此在进行考试之前要先掂量一下是否已经进行测验(用一个布尔数组来记录)接着,一门功课并不只有一个时间点,在发现考不了的时候可以将某门功课向后面拖一下来“借”点时间嘛——如果借不了答案也就出来了:不行。
对于最小值,只需要直接用最后一场考试的时间点覆盖即可(因为如果考试结束,后面的考试直接跳过,是不会覆盖答案的)最后思路总结:
顺序遍历一遍所有考试,处理一下应考考试所需时间前缀和,并与这几门考试里的最后一门的时间点进行比较,若考试时间大于测验时间则尝试将某门已考测验后延(包括本场考试。若找不到则证明无法完成所有测验,停止遍历并直接输出结果)
遍历结束后若全部考完(当心有功课没得考!)输出最后一门应考考试结束的时间点。
代码
#include <iostream>
using namespace std;
int n, m, p;
int day[100005], sub[100005];
int ans; //答案
bool accepted[100005];
struct exam {
int day, sub;
} ex[100005];
int main() {
cin >> n >> m;
for (int i = 1; i <= n; ++i)
cin >> day[i];
for (int i = 1; i <= m; ++i) {
cin >> sub[i];
sub[i] ++;
}
for (int i = 1; i <= n; ++i) {
if (day[i] == 0)
continue;
ex[++p] = {i, day[i]};
}
//处理为p个考试下的问题,利用前缀和得到准备时间(附带考试时间)
int sum = 0; //不考试的话准备时间就是0了
for (int i = 1; i <= p; ++i) {
if (accepted[ex[i].sub])
continue; //考完了就不用管了
sum += sub[ex[i].sub];
accepted[ex[i].sub] = true;
ans = ex[i].day;
while (sum > ex[i].day) { //时间不够?这下得专心复习这一科了
if (i >= p) {
cout << -1; //挤不出时间?寄
return 0;
}
for (int j = i + 1; j <= p; ++j) {
if (accepted[ex[j].sub]) {
sum -= sub[ex[j].sub];
accepted[ex[j].sub] = false;
break;
}
if (j >= p) {
cout << -1; //挤不出时间?寄
return 0;
}
}
}
}
for (int i = 1; i <= m; ++i) {
if (!accepted[i]) {
cout << -1;
return 0;
}
}
cout << ans;
return 0;
}