POJ1042 Gone Fishing

题目来源:http://poj.org/problem?id=1042

题目大意:

  John有h个小时的时间想去钓鱼。(1<=h<=16).有n个池塘(2<=n<=25),它们的分布沿着一条单行的小路。John从第一个池塘处出发,他可以沿着小路向前走,在想停下来的池塘处钓鱼,对于路径的终点没有限制。为了钓到最多的鱼,John对各个池塘做了调查。若给路径上的池塘依次编号,,则对于每个池塘,开始钓鱼时,每5分钟内期望是可以钓到f[i]条鱼,随着时间的推移,每过5分钟,可以钓到的鱼减少d[i]条。若某个5分钟的时间段内可以钓到的鱼少于等于d[i],则下一个5分钟在这个池塘就钓不到鱼了。用t[i]表示从池塘 i 到池塘 i+1 所需要的时间。单位是5分钟(==!),  即:若t[3] = 4,表示从池塘3到池塘4需要4*5=20分钟。John在每个池塘钓鱼的时间都必须是5的倍数。求期望能钓到最多鱼的钓鱼计划,并输出在每个池塘钓鱼的时间(分钟为单位)和能钓到的鱼总数。当有多个方案都是最优解时,选择在第一个湖的时间最长的方案,若仍相等,选择在第二个湖时间最长的方案,依此类推。

输入:每个测试用例,首先给出池塘数n,然后是时间h(小时为单位),接下来的两行分别有n个整数,分别表示f[i]和d[i],接下来的一行为n-1个整数,表示t[i].n为0时表示输入结束。

输出:对于每个测试用例,第一行依次输出在每个池塘的停留时间(分钟为单位),每个时间之间用逗号+空格分开。第二行输出能钓到的最多的鱼的数量,格式见Sample.


Sample Input

2 
1 
10 1 
2 5 
2 
4 
4 
10 15 20 17 
0 3 4 3 
1 2 3 
4 
4 
10 15 50 30 
0 3 4 3 
1 2 3 
0 

Sample Output

45, 5 
Number of fish expected: 31 

240, 0, 0, 0 
Number of fish expected: 480 

115, 10, 50, 35 
Number of fish expected: 724

本题可以用 DP or 枚举+贪心 的方法做。


DP的状态和状态转移:

dp[i][j]表示第j个五分钟时John位于池塘 i 的情况最多可以钓到多少鱼。

dp[i][j] = max(dp[i][j], dp[i - 1][j - t[i] - k]);

实际处理时,对于所有可行的k: dp[i + 1][j + k + t[i]] = max(dp[i + 1][j + k + t[i]], dp[i][j] + sum), 其中sum是在i+1处钓鱼k个5分钟所钓到的鱼。


 实际上枚举+贪心的方法更容易,数据量不大,所以速度更快。

我们可以把总时间分为两个部分:在路上的时间和在钓鱼的时间。由于路是单行的,所以在路上的时间取决于走的最远距离,即到达的池塘的最大编号。 剩余的时间用于钓鱼。我们可以把移动+钓鱼的混合过程拆分为移动的过程和钓鱼的过程,即指定一个池塘为终点,移动过程即从起点到终点的过程,钓鱼的过程就是从起点到终点的各个池塘中选择池塘钓鱼,因为移动过程所需的时间已经在前面考虑过了,这个时候我们就可以认为需要移动的时候可以直接瞬间到达。然后,选择到哪些池塘钓鱼的策略采用贪心的方法,每个钓鱼的5分钟都选择期望最大的那一个池塘,每在选择一个池塘钓鱼5分钟,减少相应池塘的期望,增加计划中在该池塘钓鱼的时间,然后继续选择期望最大的池塘,直到钓鱼的时间不够,或者池塘里没有鱼了。如果池塘没有鱼了,还有时间的话,把多余的时间分配给第一个池塘。

以上解释的是贪心策略,枚举所有池塘作为终点时贪心结果,再选择最优的方案即可。

由于数据规模不大,每一步贪心的时候都直接遍历数组实现了,有人提到了用堆来做,也许能快一些。

 1 //////////////////////////////////////////////////////////////////////////
 2 //        POJ1042 Gone Fishing
 3 //        Memory: 228K        Time: 110MS
 4 //        Language: C++        Result: Accepted
 5 //////////////////////////////////////////////////////////////////////////
 6 
 7 #include <iostream>
 8 
 9 using namespace std;
10 
11 int m;        //分钟数
12 int n;        //池塘数
13 int f[25];    //池塘初始鱼数
14 int d[25];    //鱼每5分钟减少数目
15 int left_fish[25];    //剩下的鱼
16 int t[24];    //每两池塘之间的路径长度
17 int plan[25];    //每个池塘的停留时间
18 int temp[25];
19 int max_cnt;    //最大期望
20 int cnt;
21 
22 inline void update() {
23     for (int i = 0; i < n; ++i) plan[i] = temp[i];
24 }
25 
26 void process() {
27     memset(plan, 0, sizeof(plan));
28     max_cnt = -1;
29     int flag = true;
30     int time_on_the_road = 0;
31     int time_fishing = m;
32     //枚举所有池塘为终点的情况
33     for (int i = 0; i < n; ++i) {
34         cnt = 0;
35         memset(temp, 0, sizeof(temp));
36         time_fishing = m - time_on_the_road;
37         for (int k = 0; k < n; ++k) left_fish[k] = f[k];
38         
39         //greedy
40         while (true) {
41             if (time_fishing < 5) break;    //时间耗完了 
42             int most = -1;
43             int lake_id;
44             for (int j = 0; j <= i; ++j) {
45                 if (left_fish[j] > most) {
46                     most = left_fish[j];
47                     lake_id = j;
48                 }
49             }
50             if (most == 0) {
51                 temp[0] += time_fishing / 5 * 5;
52                 break;    //没有鱼了
53             }
54             temp[lake_id] += 5;
55             cnt += left_fish[lake_id];
56             left_fish[lake_id] -= d[lake_id];
57             if (left_fish[lake_id] < 0) left_fish[lake_id] = 0;
58             time_fishing -= 5;
59         }
60         if (cnt > max_cnt) {
61             max_cnt = cnt;
62             update();
63         } else if (cnt == max_cnt) {
64             for (int i = 0; i < n; ++i) {
65                 if (temp[i] < plan[i]) break;
66                 if (temp[i] > plan[i])    update();
67             }
68         }
69         time_on_the_road += t[i];
70         if (time_on_the_road > m) break;    //不可能走到该池塘
71     }
72     cout << plan[0];
73     for (int i = 1; i < n; ++i) cout << ", " << plan[i];
74     cout << endl << "Number of fish expected: " << max_cnt << endl << endl;
75 }
76 
77 int main(void) {
78     while (true) {
79         cin >> n;
80         if (n == 0) break;
81         cin >> m;
82         m *= 60;    //小时转化为分钟
83         for (int i = 0; i < n; ++i) cin >> f[i];
84         for (int i = 0; i < n; ++i) cin >> d[i];
85         for (int i = 0; i < n - 1; ++i) {
86             cin >> t[i];
87             t[i] *= 5;    //转为分钟数
88         }
89         process();
90     }
91     return 0;
92 }
View Code  
posted @ 2013-08-07 21:04  小菜刷题史  阅读(3222)  评论(0编辑  收藏  举报