黑书中的经典题:枚举+贪心
把每钓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; }