CodeForces732D——Exams 详解
- Exam
-
题目描述(google翻译)
-
Vasiliy的考试期限将持续n天。他必须通过m门科目的考试。受试者编号为1至m。
大约每天我们都知道当天可以通过m个科目中的哪一个的考试。也许,有一天你不能通过任何考试。任何一天都不允许通过多个考试。
每天Vasiliy都可以通过当天的考试(需要一整天)或准备一整天的考试或休息。
关于每个主题Vasiliy知道一个数字ai - 他准备通过考试号码的天数i。 Vasiliy可以在准备考试时切换科目,没有必要在考试编号i的ai天内连续准备。他可以以任何方式混合考试准备。
您的任务是确定Vasiliy可以通过所有考试的最短天数,或确定不可能。每次考试都应该通过一次。
-
-
输入
-
第一行包含两个整数n和m(1≤n,m≤105) - 考试期间的天数和科目数。
第二行包含n个整数d1,d2,…,dn(0≤di≤m),其中di是考试的科目号,如果是 3,就代表可以考第三门。如果di等于0,则不允许在第i天通过任何考试。
第三行包含m个正整数a1,a2,…,am(1≤ai≤105),其中ai是在通过对象i的考试之前准备所需的天数。
-
- 输出
- 打印一个整数 - Vasiliy可以通过所有考试的最小天数。如果不可能,请打印-1。
- 样例输入 1
7 2
0 1 0 2 1 0 2
2 1
- 样例输出 1
5
- 样例输入 2
10 3
0 0 1 2 3 0 2 0 1 2
1 1 4
- 样例输出 2
9
- 样例输入 3
5 1
1 1 1 1 1
5
- 样例输出 3
-1
- 问题分析
- 贪心的想,也就是考试往后推,直到最后能考的那一天,如果这样复习时间都不够,那么肯定是过不了的。
- 那么二分怎么用呢,二分就用在时间上,对于一个输入天数 n,他最迟不能超过 n 天通过考试,否则就不能通过,因此利用二分时间找出答案。
- 你可能会产生这样的疑问,怎么保证程序在某一门考试的最后期限来临之前将该科目可以利用的时间都用上。这个并不难实现,只需要将每个科目的位置记录下来,比如对于第二组数据,1 首先出现,那么它的位置为 3,记为1 - 3,接下来是 2 - 4,3 - 5,2 - 7,1 - 9,2 - 10,看到了吗,只要 1 后面还有 1,它的位置就会被修改,那我们只需要根据记录的位置信息来判断接下来要考试的科目和所拥有的时间,就可以保证在考试前利用所有可用的时间。
#include <iostream> #include <cstdio> #include <cstring> #define Maxn 100001 using namespace std; int day[Maxn],exam[Maxn]; int n,m; bool deal(int mid); int main() { cin>>n>>m; int i = 1; for(i = 1; i <= n; i++) cin>>day[i]; for(i = 1; i <= m; i++) cin>>exam[i]; int l = 1; int r = n; int ans = -1; while(r >= l) { int mid = (l + r)/2; if(deal(mid)) { ans = mid; r = mid - 1; } else l = mid + 1; } cout<<ans; return 0; } bool deal(int mid) { int havetime = 0; int lastday[Maxn]; memset(lastday,-1,sizeof(lastday)); int i = 1; for(i = 1; i <= mid; i++) { if(day[i] != 0) { lastday[day[i]] = i; } } for(i = 1; i <= m ; i++) { if(lastday[i] == -1) return false; } for(i = 1; i <= mid; i++) { if(day[i] == 0) havetime++; else { if(lastday[day[i]] == i) { if(havetime >= exam[day[i]]) { havetime -= exam[day[i]]; } else return false; } else havetime++; } } return true; }