Codeforces Round #377 (Div. 2) D. Exams && codeforce 732 D 详解(贪心+二分)

54 篇文章 0 订阅



D. Exams
time limit per test
1 second
memory limit per test
256 megabytes
input
standard input
output
standard output

Vasiliy has an exam period which will continue for n days. He has to pass exams on m subjects. Subjects are numbered from 1 to m.

About every day we know exam for which one of m subjects can be passed on that day. Perhaps, some day you can't pass any exam. It is not allowed to pass more than one exam on any day.

On each day Vasiliy can either pass the exam of that day (it takes the whole day) or prepare all day for some exam or have a rest.

About each subject Vasiliy know a number ai — the number of days he should prepare to pass the exam number i. Vasiliy can switch subjects while preparing for exams, it is not necessary to prepare continuously during ai days for the exam number i. He can mix the order of preparation for exams in any way.

Your task is to determine the minimum number of days in which Vasiliy can pass all exams, or determine that it is impossible. Each exam should be passed exactly one time.

Input

The first line contains two integers n and m (1 ≤ n, m ≤ 105) — the number of days in the exam period and the number of subjects.

The second line contains n integers d1, d2, ..., dn (0 ≤ di ≤ m), where di is the number of subject, the exam of which can be passed on the day number i. If di equals 0, it is not allowed to pass any exams on the day number i.

The third line contains m positive integers a1, a2, ..., am (1 ≤ ai ≤ 105), where ai is the number of days that are needed to prepare before passing the exam on the subject i.

Output

Print one integer — the minimum number of days in which Vasiliy can pass all exams. If it is impossible, print -1.

Examples
input
7 2
0 1 0 2 1 0 2
2 1
output
5
input
10 3
0 0 1 2 3 0 2 0 1 2
1 1 4
output
9
input
5 1
1 1 1 1 1
5
output
-1
Note

In the first example Vasiliy can behave as follows. On the first and the second day he can prepare for the exam number 1 and pass it on the fifth day, prepare for the exam number 2 on the third day and pass it on the fourth day.

In the second example Vasiliy should prepare for the exam number 3 during the first four days and pass it on the fifth day. Then on the sixth day he should prepare for the exam number 2 and then pass it on the seventh day. After that he needs to prepare for the exam number 1 on the eighth day and pass it on the ninth day.

In the third example Vasiliy can't pass the only exam because he hasn't anough time to prepare for it.


 

这题刚读完题似曾相识,感觉做过,一开始想成尺取发有类似的做法,想了想发现并不是。。。但是这个类型真的是做过,从后往前贪心,好像是三个人轮流出扑克牌那题,求必胜的局数(记得跟着田忌赛马后面做的,也可以用田忌赛马也可以用这种类型的贪心),因为每一科能不能考过都是由前面复习的天数决定的,所以从后往前找。。而不是从前往后找,如果从前往后找就算前面这科符合了,说不定后面要用这天来复习而不是考试。。。详见样例2。。。这类有“顺序的”题目一般都从后往前找。。(说的不太清楚,但是希望大家自己理解,意会下,言传有点难。。。


题意:就是有需要在n天里考完m科考试,给出n天可以考的科目,如果是0就说明一科也不能考。然后给出m个数 第i个数代表第i科需要几天复习,可以考试的那天,也可以复习,问最少用几天可以把这m科考完?

思路:从后往前贪心:假如判断前x天能不能考完,我们不能从前面遍历,而是要从后面遍历,因为这科在最后面前面有的复习时间肯定比从前面枚举多,从最后一天枚举,如果这一天的科目没有考过,就用一个sum变量加上这一科复习要用的天数,这样前面如果有后面已经考过的科目的天数或者值为0的天数并且当前的sum!=0,就可以加上一天的复习时间 -》 sum--;如果枚举完了n天sum != 0说明复习不完,return 0,同时这前x天如果还有的科目没考也要return 0.从头枚举每一天x,判断前x天能不能满足要求,满足的第一个x就是最小的值,这一过程可以用二分写;


#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
const int maxn = 1e5 + 5;
int book[maxn], a[maxn], d[maxn], n, m;
int judge(int x)
{
      memset(book, 0, sizeof(book));
    int sum = 0, cnt = 0;
    for(int i = x; i >= 1; i--)
    {
        if(d[i] && !book[d[i]])  //如果这一天可以考试并且后面没有考过就进行考试,sum+上要复习的天数,前面有可以复习的天数就把sum减去一天
            sum += a[d[i]], book[d[i]] = 1, cnt++;  //cnt记录的是考过的科目数量
        else if(sum > 0)   //如果需要复习的时间不为0,就不要减去1,这一天休息
            sum--;
    }
    if(sum > 0)        //没复习完
        return 0;
    if(cnt < m) //科目不够
        return 0;
    return 1;
}
int main()
{
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; i++)
        scanf("%d", &d[i]);
    for(int i = 1; i <= m; i++)
        scanf("%d", &a[i]);
    int l = 1, r = n, mid, flag = 0;  //用二分搜索,不用从1-n逐个枚举
    while(l < r)
    {
       // cout << l << ' ' << r;
      //  getchar();
        mid = (l+r) / 2;
        if(judge(mid)) r = mid, flag = 1;
        else l = mid + 1;
    }

    /*
    if(flag) printf("%d\n", r);  //这里是坑点。。因为他可能是在最初的l或者r上是最优值。。。如果这么写就不会判断n的值,如果是l 《= r就会陷入死循环
    else printf("-1\n");
    */
    if(judge(l))  //这个跟网上学的。。。
            printf("%d\n",l);
           else if(judge(r))
            printf("%d\n",r);
           else
            printf("-1\n");

    return 0;
}



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值