二分 - Exams - CodeForces - 732D
題意:
首行輸入包括兩個整數n和m,表示總天數爲n,有m門考試。
第二行包括n個整數d1,d2,...,dn,di表示第i天能夠參加第di門考試,若di=0則表示第i天無考試。
第三行包括m個整數a1,a2,...,an,ai表示第i門考試需要復習準備ai天。
現判斷最少需要幾天能夠考完m門課程,若不能,則輸出−1。
數據範圍:
1 ≤ n, m ≤ 105,0 ≤ di ≤ m,1 ≤ ai ≤ 105Time limit:1000ms,Memory limit:262144kB
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
分析:
若能在k天內能夠考完,則k+1天也一定能夠考完。
因此二分答案。
若mid天能夠考完,則r=mid,反之更新l=mid+1。
關鍵在於check函數如何判斷mid天內能否考完。
check函數是貪心的思想。將每一門課都盡量安排在最後考,前幾天都盡量的準備考試。若能考完說明mid可行。
因此我們用一個cnt數組來標記最遲在哪一天能考第di門,pre來記錄已經準備了多少天。
則cnt[k]表示第k門最遲必須在第cnt[k]天考完。
先從mid天往前考慮,若cnt[di]還未被標記過,則更新cnt[di]=i,說明最遲在第i天能考第di門。
①、若第i天能考的科目數量di=0,那麼這一天只能準備考試,pre++。
②、若cnt[di]=i,說明第i天不是最遲的一天,故這一天也用來準備考試,pre++。
③、其他情況說明這一天是考第di門最遲的一天,而考第di門需要準備a[di]天,若a[di]<=pre,說明能把第di門考完。否則check函數直接返回false說明mid不可行。
④、最後我們還要檢查一下m門考試是否都被cnt數組標記過,若有某一門未被標記過,即cnt[i]=0,則說明[1,mid]天當中,沒有哪一天安排了第i門考試,則返回false。
⑤、否則最後返回true表示mid天內能考完全部課程。
注意: 每次check要初始化cnt數組。
代碼:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
#define ll long long
#define P pair<int,int>
#define x first
#define y second
#define inf 0x3f3f3f3f
using namespace std;
const int N = 100010;
int n,m,d[N],a[N];
bool check(int x)
{
int cnt[N];
memset(cnt,0,sizeof cnt);
for(int i=x;i;i--)
if(!cnt[d[i]])
cnt[d[i]]=i;
int pre=0;
for(int i=1;i<=x;i++)
if(!d[i]||cnt[d[i]]!=i) pre++;
else
{
if(a[d[i]]>pre) return false;
else pre-=a[d[i]];
}
for(int i=1;i<=m;i++) if(!cnt[i]) return false;
return true;
}
int main()
{
cin>>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=0,r=n+1;
while(l<r)
{
int mid=l+r>>1;
if(check(mid)) r=mid;
else l=mid+1;
}
if(l==n+1) puts("-1");
else cout<<l<<endl;
return 0;
}