题目:Exams
题意:有n天考试,每天可以考对应的科目数,0为当天不考试,考m科目,每科目需要复习天数给出,问完成所有的考试最少用几天?
思路:贪心+二分
贪心:和实际生活中一样,应先复习考试时间靠前的科目,所以从后遍历天数,累计复习时间,到第一天看是否总复习时间为0,且科目数全部完成!
i.e:0 0 1 2 3 0 2 0 1 2 如果从左看,最先考试的是1,2,3但是,1,2后面还有考试天,所以,可以认为3是最先考试科目,所以要从后往前遍历!
二分:如果按上面的贪心来找,需要从n~1枚举,但是O(n*n)这样就会超时!
所以用二分查找n~1之间的数作为每次寻找的最后那天即可!
代码:
#include <iostream>
#include <cstdio>
#include <string.h>
using namespace std;
#define MAX_N 100005
int day[MAX_N],sub[MAX_N],visit[MAX_N];
int n,m;
int fun(int x)
{
memset(visit,0,sizeof(visit));
int prepa = 0,num = 0,times = 0;
for(int i=x;i>0;i--)//倒着来,新出现的科目标记,然后将本科目要复习的时间累计上
{
if(!visit[day[i]] && day[i]!=0)
{
visit[day[i]] = 1;//标记新科目
num++;//累计科目数
prepa += sub[day[i]];//累计需要复习时间
}
else
{
if(prepa)
prepa--;//复习一天减一天
}
times++;//累计共需多长时间完成
}
if(num == m && prepa == 0)//成立条件,科目全考,且全部复习完成
return times;
return -1;}
int binarySearch()//二分查找从哪天开始倒着来!
{
int head = 1,tail = n,mid,ans=999999999,temp;
while(head <= tail)
{
mid = (head+tail)/2;
temp = fun(mid);
if(temp > 0)
tail = mid - 1;
else
head = mid + 1;
if(temp != -1)
ans = min(temp,ans);
}
if(ans == 999999999)
ans = -1;
return ans;}
int main()
{
while(cin >> n >> m)
{
for(int i=1;i<=n;i++)
cin >> day[i];
for(int i=1;i<=m;i++)
cin >> sub[i];
cout << binarySearch() << endl;
}
return 0;
}