Codeforces Round #377 (Div. 2) D. Exams

        Codeforces Round #377 (Div. 2) D. Exams

   题意:给你n个考试科目编号1~n以及他们所需要的复习时间ai;(复习时间不一定要连续的,可以分开,只要复习够ai天就行了)   然后再给你m天,每天有一个值di; 其中,di==0代表这一天没有考试(所以是只能拿来复习的); 若di不为0,则di的值就代表第i天可以考编号为di的科目 ;(当然这一天也可以不考而拿来复习) 。  问你最少能在第几天考完所有科目,无解则输出-1。

  

    题解:首先,先想想最暴力的方法:从第1天到第n天这样一天天地试,每次判断前k天能否考完所有科目(至于怎么判断前k天能否够考完所有科目下面会说)。 这样子的最坏情况的复杂度是O(n^2);  那么如果不要这样一天天地判断过去的话,要进行优化;那么,由于题目要问最小天数能考完所有科目,且天数都是升序的—这时,就可以用二分了。 二分能考完所有科目的天数,每次判断该天数能否考完。   那么问题就被简化了;原来问题是最小的天数要考完所有科目,,而现在就只需关心怎么判断用前k天时间能考完所有科目    这里用贪心的方法:建立个变量sum复习需要用到的天数 , 用第k天开始往前扫;如果di不为0(另外还要有个标记数组来标记该科目是否是第一次扫到),则将该科目所需要的复习天数加到sum上; 如果di为0,则将sum-- (前提是sum不为0)。  扫完后,用标记数组判断是否所有科目都考了且sum是否为0 ; 若sum不为0,说明复习天数是不够用的。  最后复杂度是为O(nlogn)的。

   ( ps: 可能有的人会觉得从后往前扫,只有遇到的科目第一次出现时才加入到sum中,但这些都是k天内各科目的最后"期限",虽然判断到用k天可行,但可能不是最优的(一开始自己就在这上面给坑了=_=)。    但是,因为上面已经有二分了,所以我们这后面只要关注在能不能用k天将所有科目考完,而不用理会它是不是最优的! )

    下面是AC代码:  这一题的后台数据其实是有点水的,,如果发现代码有Bug的话欢迎各位提出  (o゜▽゜)o☆

 1 /**
 2 * @author geek1116
 3 */
 4 #include <iostream>
 5 #include <cstdio>
 6 #include <cstring>
 7 #include <cmath>
 8 #include <algorithm>
 9 #include <queue>
10 #include <stack>
11 #include <vector>
12 #include <utility>
13 #include <map>
14 #include <set>
15 const int inf=0x3f3f3f3f;
16 const double PI=acos(-1.0);
17 const double EPS=1e-10;
18 const int MAXN=1e5;
19 using namespace std;
20 typedef long long ll;
21 typedef pair<int,int> P;
22 
23 int n,m;
24 int d[MAXN+10];
25 int a[MAXN+10];
26 int book[MAXN+10]; //标记数组
27 
28 bool judge(int x) //判断用前x天是否可行
29 {
30     memset(book,0,sizeof(book));
31     int sum=0,cnt=0;
32     //
33     for(int i=x;i>=1;i--)
34     {
35         if(d[i])
36         {
37             if(!book[d[i]]) sum+=a[d[i]],book[d[i]]=1,cnt++;
38             else if(sum!=0) sum--;
39         }
40         else if(sum!=0)
41             sum--;
42     }
43     //
44     if(sum||cnt!=m) return false;
45     else return true;
46 }
47 int main()
48 {
49     //freopen("input.txt","r",stdin);
50     scanf("%d%d",&n,&m);
51     for(int i=1;i<=n;i++) scanf("%d",&d[i]);
52     for(int i=1;i<=m;i++) scanf("%d",&a[i]);
53     //
54     int l=1,r=n,mid;
55     while(l<r)          //二分结束天数
56     {
57         mid=(l+r)>>1;
58         if(judge(mid)) r=mid;
59         else l=mid+1;
60     }
61     //
62     if(judge(l)) printf("%d
",l);
63     else if(judge(r)) printf("%d
",r);
64     else printf("%d
",-1);
65     return 0;
66 }