继续向前做题。
这个题在http://poj.org/problem?id=1042
还是粘一下题
原题
题意分析
首先要弄清楚,在我看来是最重要的一句话是,
每个鱼池只经过一次,只能按照从左向右的顺序访问。并不是每个池塘都必须停下来钓鱼。
这句话太重要了,没有这句话,这个题直接就上难度了。
也就是说如果有1,2,3,4个池塘。那么可以选择只钓1,3两个池塘的。2只是路过。
不能按照1,4,2,的顺序来进行钓鱼活动。
算法设计
枚举
我是看了刘汝佳的书,其实我一直没想明白,为什么用贪心,如何用,心里面一直都不清楚。
仔细想了才清楚。因为每次钓鱼只能选择左半段进行活动。那么枚举左半段即可。
第一次枚举:只能在1,池塘里面钓鱼。
第二次枚举:只能在1,2池塘里面钓鱼。
第三次枚举:只能在1,2,3池塘里面钓鱼。
第i次枚举:只能在1,2,3,~~~i池塘里面钓鱼。
直到最后一个池塘。
贪心
对于每一次枚举用贪心算法。由于对于第i次枚举,对于所有的枚举到的池塘(1~i)来说,都是要路过的。并且都是从左至右的方式进行路过。
那么跑路的时间就是恒定的。余下的时间直接用来钓鱼。去掉了跑路的时间,题又简单了一半。
接下来看怎么贪心。
这里有个很大的限制是,钓鱼是必须从左边的池塘向右边的池塘一个接一个地钓鱼。
首先我们想一下如果不是一个接一个地钓鱼会是什么结果。
我们每次都是选择鱼最多的来钓。其实结果都是一样的。
比如
第一次我们选择在1号池塘钓鱼。t1
第二次我们选择在2号池塘钓鱼。t2
第三次我们又选择在1号池塘钓鱼。t3
与
第一次我们在1号池塘钓鱼用了t1+t3时间
第二次我们走到2号池塘钓鱼用了t2时间。
效果是一样的。钓鱼的数量是一样的。
所以贪心的策略就是每次钓鱼的时候选择鱼最多的来钓鱼。(这里可以用一个最大堆来做)
限制条件
如果两个池塘的鱼一样多。那么选择最左边的鱼塘来钓鱼(人毕竟是懒的,能不走路,谁TMD走来走去啊)。这个也比较好弄。直接在class lake的大小比较中加入ID号则可。
这里看到网上有很多人对于为什么当鱼减少至负数之后,要设置为0不理解。
这里分析一下原因。
如果减少为负数不置为0。那么假设所有的鱼塘的鱼都被最后一次钓鱼钓鱼成了负数。
比如
-4, -3, -2 -1 0
这时候如果还是按照少原则来进行选择,那么会选择最后一个池塘来进行停留。
但是实际上每个池塘的鱼都是0.
所以应该选择第一号池塘进行停留。
代码
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<set> #include<iostream> using namespace std; class lake { public: int id; int fishes; int dec; public: lake(){} lake(int a, int b, int c):id(a), fishes(b), dec(c){} friend bool operator < (const lake &a, const lake &b) { if (a.fishes == b.fishes) return a.id < b.id; return a.fishes > b.fishes; } }; int n, h, f[30], d[30], t[30]; int max_fish_times[30]; int temp_fish_times[30]; int main(void) { while (scanf("%d", &n) != EOF && n) { scanf("%d", &h); h *= 12; for (int i = 0; i < n; ++i) scanf("%d", f + i); for (int i = 0; i < n; ++i) scanf("%d", d + i); for (int i = 1; i < n; ++i) scanf("%d", t + i); for (int i = 1, s = 0; i < n; ++i) {s += t[i]; t[i] = s;} int max_fishes = -1; memset(max_fish_times, 0, sizeof(max_fish_times)); for (int stop_lake = 0; stop_lake < n; ++stop_lake) { const int max_fishing_times = (h - t[stop_lake]); set<lake> min_lakes; for (int i = 0; i <= stop_lake; ++i) min_lakes.insert(lake(i, f[i], d[i])); int sum_fishes = 0; memset(temp_fish_times, 0, sizeof(temp_fish_times)); for (int fish_time_th = 0; fish_time_th < max_fishing_times; ++fish_time_th) { //选择最多的进行钓鱼。 lake temp_lake = *(min_lakes.begin()); min_lakes.erase(min_lakes.begin()); sum_fishes += temp_lake.fishes; temp_fish_times[temp_lake.id]++; temp_lake.fishes -= temp_lake.dec; if (temp_lake.fishes <= 0) temp_lake.fishes = 0; min_lakes.insert(temp_lake); } if (sum_fishes > max_fishes) { memcpy(max_fish_times, temp_fish_times, (n<<2)); max_fishes = sum_fishes; } } for (int i = 0; i < n - 1; ++i) printf("%d, ", max_fish_times[i]*5); printf("%d\n", max_fish_times[n-1]*5); printf("Number of fish expected: %d\n\n", max_fishes); } return 0; }