poj 1042 Gone Fishing dp或枚举贪心

【问题描述】
约翰是个垂钓谜,星期天他决定外出钓鱼h小时(1≤h≤16),约翰家附近共有n个池塘(2≤n≤25),这些池塘分布在一条直线上,约翰将这些池塘按离家的距离由近到远编上号,依次为L1,L2,…,Ln,约翰家门外就是第一个池塘,所以他到第一个池塘是不用花时间的。

约翰可以任选若干个池塘由近到远地垂钓,并且在每个池塘他都可以呆上任意长的时间,但呆的时间必须为5分钟的倍数(即5分钟为一个单位时间),已知从池塘Li到池塘Li+1要化去约翰ti个单位时间。每个池塘的上鱼率预先也是已知的,池塘Li在第一个单位时间内能钓到的鱼为Fi(0≤Fi≤100),并且每当他在某一个鱼塘呆上一个单位时间后,该鱼塘单位时间内能钓到的鱼将减少一个常数di(0≤di≤100)。

现在请你编一个程序计算约翰最多能钓到多少鱼。

【输入格式】
第一行为一个整数n,第二行为一个整数h,第三行为n个用空格隔开的整数,表示Fi(i=1,2,…,n),第四行为n个用空格隔开的整数,表示di(i=1,2,…,n),第五行为n-1个用空格隔开的整数,表示ti(i=1,2,…,n-1)

【输出格式】
一个整数,表示约翰最多能钓到鱼的数量。

【输入样例】

2
1
10 1
2 5
2
 
 
  • 1
  • 2
  • 3
  • 4
  • 5

【输出样例】

31
 
 
  • 1

【数据范围】
2≤n≤25 , 1≤h≤16 , 0≤ti≤100 , 0≤Fi≤100 , 0≤di≤100

解法一: DP

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分钟所钓到的鱼。(注意细节)!

代码:

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
using namespace std;

const int MAXN = 36;
int dp[MAXN][2400], f[MAXN], d[MAXN], n, t[MAXN];

int main() {
    int i, j, SumT, res[MAXN][2400];//res用来保存路径
    while (scanf("%d", &n) && n) {
        scanf("%d", &SumT);
        SumT *= 12;
        for(i = 1; i <= n; i++)
            scanf("%d", &f[i]);
        for(i = 1; i <= n; i++)
            scanf("%d", &d[i]);
        for(i = 1; i < n; i++)
            scanf("%d", &t[i]);
        int nowT = SumT;
        memset(dp, 0, sizeof(dp));
        memset(res, 0, sizeof(res));
        for(i = 1; i <= n; i++) { //枚举要钓鱼的前i个池塘
            for (j = 1; j <= nowT; j++) {//枚举时间
                dp[i][j] = dp[i-1][j];
                int p = 1;
                int tmp = f[i];
                while (dp[i-1][j-p]+tmp > dp[i][j] && j-p >= 0) {
                    dp[i][j] = dp[i-1][j-p] + tmp;
                    res[i][j] = p;
                    tmp += (f[i]-p*d[i]) > 0 ? (f[i]-p*d[i]):0;
                    p++;
                }
            }
            if (i != n) nowT -= t[i];//nowT记得更新
        }
        i = n;
        j = nowT;
        int ii = n;
        int maxx = dp[ii][nowT];
        while (ii != 1) {//找出钓鱼最多的那种方案
            nowT += t[--ii];
            if (dp[ii][nowT] >= maxx) {//这里是>=,要尽量钓前面的鱼。
                maxx = dp[ii][nowT];
                i = ii;
                j = nowT;
            }
        }
        int result[MAXN] = {0};//注意置零
        while (i != 0) {//搞出result,就是每个池塘钓的鱼
            result[i] = res[i][j]*5;
            SumT -= res[i][j];
            SumT -=  t[i-1];
            j -= res[i][j];
            i--;
        }
        result[1] += SumT*5;//这里还剩的时间说明钓不到鱼了,全部加到第一个池塘
        for(i = 1; i <= n; i++)
            if (i != n)
                printf("%d, ", result[i]);
            else printf("%d\n", result[i]);
        printf("Number of fish expected: %d\n\n", maxx);
    }
    return 0;
}

方法二: 枚举+贪心

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

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

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

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

代码就不发了,注意细节就好了!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值