黑书中的经典题:枚举+贪心
把每钓5分钟鱼称为钓一次鱼。首先枚举John需要走过的池塘的数目X,即从池塘1走到池塘X。减去路上花去的时间T=sum(Ti) i=1...X-1,这样我们可以认为John能从一个池塘"瞬间转移"到另一个池塘,即在任意一个时刻都可以在池塘1到池塘X中任选一个钓一次鱼(很重要)。现在采用贪心策略,每次选择鱼最多的池塘钓一次鱼。对于每个池塘来说,由于在任何时候鱼的数目只和John在该池塘里钓鱼的次数有关,和钓鱼的总次数无关,所以这个策略是最优的。假设一共允许钓k次鱼,那么每次在N个池塘中选择鱼最多的一个钓。总的时间复杂度为O(kn^2)。(黑书中的解释)
在最后的结果中,第一个最大值所对应的在每个池塘的钓鱼时间为题目要求的情况,因为如果John在后面的池塘钓了鱼,那么前面相应的时间就会减少。最后注意池塘中没有鱼的情况。
具体见代码
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define MAXN 26
int main()
{
int n, h, tmp, sum, max;
int i, j, k, p;
int F[MAXN], f[MAXN], d[MAXN], t[MAXN] = {0}, ans[MAXN], ANS[MAXN];
// f[MAXN]保存每次湖泊剩下的鱼,ans[MAXN]保存每次枚举中各个湖泊呆的时间,ANS[MAXN]表示每次枚举后的在湖泊呆的最佳时间
while (scanf("%d", &n) != EOF && n)
{
scanf("%d", &h);
h *= 12;
for (i = 0; i < n; i++)
scanf("%d", &F[i]);
for (i = 0; i < n; i++)
scanf("%d", &d[i]);
for (i = 1; i < n; i++)
{
scanf("%d", &tmp);
t[i] = tmp + t[i-1]; //到达第i个湖所用的时间
}
memset(ANS, 0, sizeof(ANS));
for (max = 0, i = 1; i <= n; i++) // 枚举需要走过的湖泊的数目
{
memset(ans, 0, sizeof(ans));
for (j = 0; j < i; j++)
f[j] = F[j]; //每次循环时,要恢复湖泊中原有的鱼的数目
for (j = 0, sum = 0; j < h - t[i-1]; j++) //h-t[i-1]表示可以钓的次数
{
for (p = 0, k = 1; k < i; k++) //每次选一个鱼最多的湖泊钓一次鱼
if (f[k] > f[p])
p = k;
if (f[p] <= 0) //该湖泊剩下的鱼少于零时,将剩下的所有时间都加到呆在第一个湖泊的时间上
{
ans[0] += h - t[i-1] - j;
break;
}
sum += f[p];
f[p] -= d[p];
ans[p]++; //累加在该湖呆的时间
}
if (sum > max)
{
max = sum;
memcpy(ANS, ans, sizeof(ans));
}
if (sum == max)
{
for (j = 0; j < i; j++)
if (ans[j] != ANS[j])
break;
if (ans[j] > ANS[j]) //钓的鱼的数目相同时,选最近的湖泊呆比较长的时间
memcpy(ANS, ans, sizeof(ans));
}
}
for (i = 0; i < n - 1; i++)
printf("%d, ", ANS[i]*5);
printf("%d\nNumber of fish expected: %d\n\n", ANS[n-1]*5, max);
}
return 0;
}