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 }