POJ 1042 Gone Fishing (DP)

解题思路 (DP)

假如在给定时间h内,最远到达第个i湖时,最多捕鱼数为F(i, h), 那么问题的解为 

MAX{F(i, h)}, i=1...n

考虑最远到达第i个湖时,除去路途中的时间,最多的有效捕鱼时间为 hi = h - sum(t[0]+...+t[i-1]), 那么:
F(i, h) <=> maxfish(i, hi) 
且有
MAX{F(i, h)} = MAX{maxfish(i, hi)} = MAX{maxfish(1,h1), ..., maxfish(n,hn)}

这里maxfish(i, k)表述为原问题的简化形式:
不考虑湖之间的消耗时间(即t0=t1=...=t[i-1]=0),给定时间k内,在i个湖的最多捕鱼数。

建立递推关系,maxfish(i, k)取下列最大者:
{ k时间内在i-1个湖的最多捕鱼数; k时间内在第i个湖捕鱼时间大于0的所有情况的最多捕鱼数 }

maxfish(i, k) = MAX{ maxfish(i-1,k), lakefish(i,j)+maxfish(i-1,k-j) }, 1<j<=k 
lakefish(i,j)表示在第i个湖捕鱼j时间能捕到的总鱼数。

原问题MAX{maxfish(i, hi)},只需构造maxfish(n,hn)的解,maxfish(1,h1),...,maxfish(n-1,h[n-1])为过程中更小规模的子问题的解,这n个不同规模的子问题的解的最大者,即为原问题的解。

这个思路的关键在于,将问题简化为不考虑湖之间花费的时间,能更容易使用动态规划来求解。
注意点:
1. 计算过程中h时间转化为单位时间h=h*12, 求解完后输出时间均*5

2. 剩余时间放到第一个湖

代码

#include <iostream>
#include <memory.h>

using namespace std;

int f[26];
int d[26];
int t[25];

int lakefish[26][193];
int maxfish[26][193];  
int spent[26][193];  
int spentMax[26];

int main(int argc, char** argv)
{
  int cases = 0;
  int n, h;
  while (cin >> n)
  {
    if (n > 0 && cases > 0)
    {
      cout << endl;
    }

    if (n == 0) { break; }

    ++ cases;

    // input

    cin >> h;
    h = h * 12;
    for (int i = 1; i <= n; ++i)
    {
      cin >> f[i];
    }
    for (int i = 1; i <= n; ++i)
    {
      cin >> d[i];
    }
    for (int i = 1; i < n; ++i)
    {
      cin >> t[i];
    }

    // lakefish(i,j)

    memset(lakefish, 0, sizeof(lakefish));
    int fk = 0;
    for (int i = 1; i <= n; ++i)
    {
      for (int k = 1; k <= h; ++k)
      {
          fk = f[i] - d[i] * (k - 1);
          if (fk > 0)
          {
            lakefish[i][k] = lakefish[i][k-1] + fk;
          }
          else
          {
            lakefish[i][k] = lakefish[i][k-1];
          }
      }
    }

    // 1. maxfish(i, k) = MAX{ maxfish(i-1,k), lakefish(i,j)+maxfish(i-1,k-j)}, 1<j<=k 
    // 2. result = MAX{maxfish(1,h1), ..., maxfish(n,hn)}

    memset(maxfish, 0, sizeof(maxfish));
    memset(spent, 0, sizeof(spent));
    memset(spentMax, 0, sizeof(spentMax));

    int hi = h;
    int fishij = -1;
    int maxfishnk = -1, maxfishn = -1, maxfishh = -1;
    for (int i = 1; i <= n; ++i)
    {
        hi = hi - t[i-1]; 
        for (int k = 1; k <= hi; ++k)
        {
            maxfish[i][k] = maxfish[i-1][k];
            spent[i][k] = 0;
            for (int j = 1; j <= k; ++j)
            {
               fishij = lakefish[i][j] + maxfish[i-1][k-j];
               if (fishij > maxfish[i][k])
               {
                  maxfish[i][k] = fishij;
                  spent[i][k] = j;
               }
            }

            if (k == hi && maxfish[i][k] > maxfishnk)
            {
              maxfishnk = maxfish[i][k];
              maxfishn = i;
              maxfishh = k;
            }
        }
    }

    // output

    int i = maxfishn;
    int k = maxfishh;
    while (i > 0)
    {
      spentMax[i] = spent[i][k];
      k = k - spentMax[i];
      if (i == 1) { spentMax[i] += k; }
      -- i;
    }
    for (int i = 1; i <= n; ++i)
    {
      if (i > 1) { cout << ", "; }
      cout << spentMax[i]*5;
    }
    cout << endl << "Number of fish expected: " << maxfishnk <<endl;
  }

  return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值