POJ1042钓鱼问题解答

问题描述

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时表示输入结束。
例如:

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

输出

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

解题思路

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

具体代码

#include <iostream>
#include <cstring>
#include <climits>
#define N 25
using namespace std;
int main(void) 
{
    int n, h;
    int fi[N], di[N], ti[N];//fi[]期望钓鱼数, di[]每五分钟减少鱼数, ti[]位移时间
    int time[N];//在当前湖停下来时在每个湖停留的时间
    int best[N];//最优解对应的在每个湖停留的时间
    int bestFishNum;//最优解钓鱼总数
    int fishNum;//当前钓鱼总数
    while (cin >> n && n)
    {
        cin >> h;
        h *= 12;//共有h个5分钟
        for (int i = 0; i < n; i++) cin >> fi[i];//期望钓鱼数
        for (int i = 0; i < n; i++) cin >> di[i];//每五分钟减少鱼数
        for (int i = 0; i < n - 1; i++) cin >> ti[i];//位移时间
        bestFishNum = INT_MIN;
        memset(best , 0 , sizeof best);//将best数组的值全部设为0 
        for (int p = 0; p < n; p++) {//穷举最终停下的湖的位置
            memset(time , 0 , sizeof(time));
            fishNum = 0;
            int remainingTime = h;//当前剩余时间
            int remainingFish[N];//当前在每个湖剩余能钓的鱼的数目
            for (int i = 0; i < n; i++)
                remainingFish[i] = fi[i];//初始化
            for (int j = 0; j < p; j++)
                remainingTime -= ti[j];//先减去路程,之后假设John可以在lake 0到lake i之间随意位移
            while (remainingTime > 0) {//直到时间用尽
                int max = INT_MIN;
                int pos = 0;//当前能钓鱼最多的位置
                for (int j = 0; j <= p; j++)//每次选出能钓鱼最多的位置
                    if (remainingFish[j] > max) 
                    {
                        max = remainingFish[j];
                        pos = j;//找到位置
                    }
                time[pos]++;//对应时间++
                fishNum += remainingFish[pos];//累加钓鱼数
                remainingFish[pos] =remainingFish[pos]- di[pos]>0?remainingFish[pos]-di[pos]:0;
                remainingTime--;
            }
            if (fishNum > bestFishNum)
            {
                bestFishNum = fishNum;
                for (int i = 0; i < n; i++)
                {
                    best[i] = time[i];
                 }
            }
        }
        for (int i = 0; i < n - 1; i++)
            cout << best[i] * 5 << ", ";
        cout << best[n - 1] * 5 << endl;//注意输出格式
        cout << "Number of fish expected: " << bestFishNum << endl << endl;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值