您的位置:首页 > 编程语言 > Go语言

ACM 算法艺术与信息学竞赛 1.2.2 钓鱼 Gone Fishing

2011-11-10 21:08 351 查看
继续向前做题。

这个题在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;
}


注意两点:1、比较函数的设计。2、set的使用。set其实默认是最小堆,所以要更改<号的定义。(变成大于的意思,就变成了最大堆)。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: