Description
期末考试快到了,qdu的考试周一共有n天,你要在这n天里考m门课,这m门课从1到m编号。
我们知道这n天内的每一天可以考哪一门课,当然,也有可能有一天不能考试。 每天只能考一次试。
你每天可以做三件事,考试、复习、或者休息。
每门课都有一个需要复习的天数,比如第i门课需要复习ai天,只有当你总共复习了ai天的第i门课,你才能在考试中通过这第i门课。当然,你也不用非得连续几天都复习一门课,可以穿插着复习,只要最后的天数满足就可以了。
为了尽早回家,你想计算出考完这m门课最少需要多少天?当然每门课都要考通过。要是怎么也不能全部通过,那就输出-1。
还是不太理解题意的话,就去看hint。
Input
第一行两个整数 n 和 m (1 ≤ n, m ≤ 105) — n天,m门课。
第二行n个整数 d1, d2, ..., dn (0 ≤ di ≤ m), di 表示这一天可以考第di 门课。如果 di 为 0,那么这一天只能复习或休息,不能考试。
第三行m个整数 a1, a2, ..., am (1 ≤ ai ≤ 105), ai 表示通过第i门课至少要复习准备这门课多少天。
Output
输出一个整数。 — 最少多少天可以通过全部的考试。 如果不可能输出-1。
Sample Input
Sample Output
Hint
第一个样例: 第一天和第二天复习科目 1 然后在第五天通过, 第三天复习科目2然后在第四天通过。
第二个样例: 前四天复习科目 3 然后在第五天通过。第六天复习科目 2 然后在第七天通过。第八天复习科目1然后在第九天通过。
第三个样例:时间不够根本通过不了考试。
这个题一开始用深搜,搜索每种可能的情况,但是超时,不知道剪枝以后能不能过,后来看了网上的代码都说是用贪心+二分,自己几乎没做过贪心的题目,对贪心不是很理解,这算是自己的第一个贪心的题目吧,一开始做这个题完全没想到要二分,枚举可能的天数,自己对贪心,二分还是没有理解好
看了大牛们的代码,几乎是照抄的,只是变量的名字不一样而已
AC代码:
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<string>
#include<algorithm>
#include<stack>
#include<queue>
#define MAX 100005
#define INF 99999999
using namespace std;
int n,m,flag,days;
int can[MAX];//今天可以干什么
int need[MAX];//该门课需要复习的天数
int test[MAX];//该门课是否已经考过了,0代表没有考过
int check(int num)
{
memset(test,0,sizeof(test));
int cur=num-1;//cur为当前剩余的可以复习的天数
for(int i=num;i>=1;i--)
{
cur=min(cur,i-1);//当前剩余可以复习的天数由两个判断,一是当前的天数-1,二是减去已经复习和考试的天数
if(can[i] && test[can[i]]==0 && cur>=need[can[i]])//can[i]表示考哪一门,也就是说这一
{ //天是可以考试的,并且该场考试没有考过
test[can[i]]=1; //并且剩余复习的天数能够复习完该门课
cur-=need[can[i]]+1;//这里的1为下次考试所占的一天,所以在剩余的复习的天数里,要去除考试的那天
}
}
for(int i=1;i<=m;i++)//判断是否所有的科目都已经考过了
if(test[i]==0)
return 0;
return 1;
}
int main()
{
int ans=-1;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&can[i]);
for(int i=1;i<=m;i++)
scanf("%d",&need[i]);
int h=1,r=n,mid;
while(h<=r)
{
mid=(h+r)>>1;
if(check(mid))
ans=mid, r=mid-1;
else h=mid+1;
}
printf("%d\n",ans);
return 0;
}