http://acm.sdut.edu.cn/onlinejudge2/index.php/Home/Contest/contestproblem/cid/2326/pid/4072
解题思路:
题目要求最短时间完成所有的考验,如果直接去求最短时间应该是不太现实的,因为这种思路既不符合贪心,又不能dp。仔细看不难发现,如果假设一个天数x,是可以通过从后往前贪心的方法去验证x天内是否可以完成所有的考验。
具体贪心方法是:假如x天之前都没有进行考验,那么对于第x天有x-1天的“空闲天”可以准备第x天的考验,于是从第x天向第0天判断每一天是不是有未完成考验,如果有就从空闲天中减去这个考验所需要的准备天数,依次维护空闲天数进行判断,最后判断一下是不是所有的考验都通过了即可。
但是如果从1到n枚举天数x是o(n^2)的复杂度,肯定是不行的,然后又发现假设天数y可以完成,那么天数大于y的都能完成,同样加入天数y不能完成,小于y的都不能完成,这就有了满足了二分判断所需要的性质,于是二分判断,复杂度满足题意。
代码:
#include <stdio.h>
#include <string.h>
const int Max = 1e6 + 100;
int vis[Max];
int data[Max], need[Max];
int isok(int n, int m){//判断n天是否可以完成m项考验
int ans = n - 1, mem = 0, num = 0;
memset(vis, 0, sizeof(vis));
for (int a = n; a >= 0; a--){
if (data[a] != 0 && !vis[data[a]] && ans >= need[data[a]]) {
ans -= need[data[a]];
mem += need[data[a]];
vis[data[a]] = 1;
if (++num == m)return 1;
ans--;
}
else if (mem > 0)mem--;
else ans--;
if (ans <= 0)
break;
}
return 0;
}
int main(){
int T, n, m;
scanf("%d", &T);
while (T--){
scanf("%d%d", &n, &m);
for (int a = 1; a <= n; a++)scanf("%d", &data[a]);
for (int a = 1; a <= m; a++)scanf("%d", &need[a]);
int l = 1, r = n, ans = -1;
while (l <= r){//二分判断
int mid = (l + r) / 2;
if (isok(mid, m)){
ans = mid;
r = mid - 1;
}
else{
l = mid + 1;
}
}
printf("%d\n", ans);
}
return 0;
}