1373:鱼塘钓鱼(fishing)

【算法分析】

解法1:区间动规

该人只会从编号小的鱼塘走到编号大的鱼塘,不存在往回走的情况(从编号大的鱼塘走到编号小的鱼塘)。

  • 如果他仅仅往回走但不在任何鱼塘停留,那么这与不往回走钓到的鱼的数量相同,往回走是不必要的。
  • 如果存在往回走的情况,那么一定存在该人从某个第x 号鱼塘走回到第y 号鱼塘,其中y < x ,而且在第y 号鱼塘停留t y 分钟钓鱼。
  • 该情况可以由以下走法替代:先走到第y 号鱼塘,停留t y 分钟钓鱼,而后走到第x 号鱼塘。这样做还能省下往回走的时间。
  1. 状态定义

集合:钓鱼方案

限制:最远走到第几个鱼塘,经过总时间

属性:钓鱼数量

条件:最大

统计量:钓鱼数量

状态定义:dp[i][j]:最远走到第i鱼塘,消耗时间j,可以钓到鱼的最大数量。

初始状态:dp[0][j]=0:在前0个鱼塘中钓鱼,消耗时间j,可以钓到鱼的最大数量为0。

  1. 状态转移方程

集合:最远走到第i鱼塘,消耗时间j的钓鱼方案数。

分割集合:根据在第i个鱼塘钓鱼的时间来分割集合

记在第i鱼塘第1分钟能钓到的鱼为fish[i],第i鱼塘每分钟鱼减少量为de[i],

可以预处理出在第i鱼塘钓鱼j分钟能钓到的鱼为f[i][j]以及在第i鱼塘能钓到鱼的最大时间mxTime[i]。

f[i][0] = 0;

for(int j = 1; fish[i] > 0; j++)

{

f[i][j] = f[i][j-1] + fish[i];

fish[i] -= de[i];

mxTime[i] = j;

}

第i鱼塘走到第i+1鱼塘的时间为t[i],用求前缀和的方法预处理出从第1鱼塘走到第i鱼塘(只走路不钓鱼)的时间st[i]。

总使用时间j,从第i-1鱼塘出发到第i鱼塘消耗时间t[i-1],那么走到第i鱼塘后,剩余可分配的时间为j-t[i-1]

  • 如果在第i鱼塘钓鱼0分钟,那么在前i-1个鱼塘消耗的时间为j-t[i-1],最大钓鱼数量dp[i][j] = dp[i-1][j-t[i-1]]
  • 如果在第i鱼塘钓鱼1分钟,那么在前i-1个鱼塘消耗的时间为j-t[i-1]-1,最大钓鱼数量dp[i][j] = dp[i-1][j-t[i-1]-1]+f[i][1]
  • 如果在第i鱼塘钓鱼2分钟,那么在前i-1个鱼塘消耗的时间为j-t[i-1]-2,最大钓鱼数量dp[i][j] = dp[i-1][j-t[i-1]-2]+f[i][2]
  • ...
  • 如果在第i鱼塘钓鱼k分钟,那么在前i-1个鱼塘消耗的时间为j-t[i-1]-k,最大钓鱼数量dp[i][j] = dp[i-1][j-t[i-1]-k]+f[i][k]
  • k的最小值为0,最大时为mxTime[i],超过该时间就钓不到鱼了。同时k要满足小于等于总时间j减去从第1鱼塘一直不钓鱼走到第i鱼塘的时间st[i],即k <= j-st[i]
  • 以上所有情况取最大值

最远走到哪个鱼塘,都可能钓到最多的鱼。

记输入的总鱼塘数量为n,截止时间为endT,

最后求dp[1][endT], dp[2][endT], ..., dp[n][endT]中的最大值,就是最大钓鱼数量。

【参考代码】

#include <bits/stdc++.h>
using namespace std;
#define N 105
#define T 1005
int dp[N][T];//dp[i][j]:在前i个鱼塘中钓鱼,消耗时间j,可以钓到鱼的最大数量。
int fish[N], de[N], f[N][T], t[N], st[N], mxTime[N];
int n, endT, mxFish;
int main()
{
	cin >> n;//n:鱼塘数量 
	for(int i = 1; i <= n; ++i)
		cin >> fish[i];//fish[i]:第1分钟第i鱼塘可以钓到的鱼的数量
	for(int i = 1; i <= n; ++i)
		cin >> de[i];//dec[i]:每过一分钟鱼可以钓到的鱼减少的数量
	for(int i = 1; i <= n; ++i)
		for(int j = 1; fish[i] > 0; ++j)
		{
			f[i][j] = f[i][j-1] + fish[i];//f[i][j]:在第i鱼塘钓鱼j分钟能钓到的鱼
			fish[i] -= de[i];
			mxTime[i] = j;//mxTime[i]:在第i鱼塘能钓到鱼的最大时间(超过这一时间就钓不到鱼了) 
		}
	for(int i = 1; i <= n-1; ++i)
	{
		cin >> t[i];//t[i]:从第i鱼塘走到第i+1鱼塘的时间
		st[i+1] = st[i] + t[i];//st[i]:从第1鱼塘走到第i鱼塘的时间 
	}
	cin >> endT;//endT:截止时间 
	for(int i = 1; i <= n; ++i)//i:鱼塘号 
		for(int j = 1; j <= endT; ++j)//j:消耗时间 
			for(int k = 0; k <= mxTime[i] && k <= j-st[i]; ++k)//k:在第j鱼塘钓鱼k分钟 
				dp[i][j] = max(dp[i][j], dp[i-1][j-t[i-1]-k] + f[i][k]);
	for(int i = 1; i <= n; ++i)
		mxFish = max(mxFish, dp[i][endT]);
	cout << mxFish;
	return 0;
}

解法2:贪心

假定最远走到第i个鱼塘,由于不用走回头路,因此花费在路上的时间已经确定了。而后安排在各个鱼塘钓鱼的时间。

  • 选择当前各个鱼塘中钓鱼1分钟能钓到最多鱼的鱼塘,记录要在这个鱼塘钓鱼1分钟。
  • 在该鱼塘钓鱼1分钟后,该鱼塘下一分钟能钓到的鱼会减少。更新该鱼塘1分钟钓鱼获得的鱼的数量。
  • 而后再在当前各个鱼塘中选择钓鱼1分钟能钓到最多鱼的鱼塘,记录要在这个鱼塘钓鱼1分钟。而后更新该鱼塘每分钟钓鱼数量。
  • 重复此过程,直到所有时间都已分配。
  • 根据表格中的记录,得知在每个鱼塘的总钓鱼时间

实际执行过程为:从第1鱼塘开始向第i鱼塘走,到第x鱼塘时,在第x鱼塘钓鱼时间为记录中在第x鱼塘的总钓鱼时间,直到走到鱼塘i。

总钓鱼数量为:在执行记录的过程中,记录了的每分钟钓鱼数量的加和。

选择"能钓到最多鱼的鱼塘"的过程,可以使用优先队列来完成,使用优先队列求最值的复杂度为 O(logn)

最远走到的鱼塘从1循环到i,求出每种情况下的总钓鱼数量,比较得到最大值,即为结果。

【参考代码】

#include <bits/stdc++.h>
using namespace std;
#define N 105
int fish[N], de[N], st[N], f[N];
int n, te, endT, mxFish;
struct Cmp
{
	bool operator () (int a, int b)
	{
		return f[b] > f[a];//鱼塘每分钟钓鱼数量更高的鱼塘更优先 
	}
}; 
int main()
{
	cin >> n;//n:鱼塘数量 
	for(int i = 1; i <= n; ++i)
		cin >> fish[i];//fish[i]:第1分钟第i鱼塘可以钓到的鱼的数量
	for(int i = 1; i <= n; ++i)
		cin >> de[i];//dec[i]:每过一分钟鱼可以钓到的鱼减少的数量
	for(int i = 1; i <= n-1; ++i)
	{
		cin >> te;//te:从第i鱼塘走到第i+1鱼塘的时间
		st[i+1] = st[i] + te;//st[i]:从第1鱼塘走到第i鱼塘的时间 
	}
	cin >> endT;//endT:截止时间
	for(int i = 1; i <= n; ++i)//最远走到第i鱼塘
	{
		memcpy(f, fish, sizeof(fish));//将fish数组的内容复制到f数组中 
		priority_queue<int, vector<int>, Cmp> pq;//优先队列中保存的是鱼塘编号,每分钟钓鱼数量更高的更优先 
		for(int j = 1; j <= i; ++j)
			pq.push(j);
		int fishNum = 0;//可分配时间为总时间减去走到鱼塘i的时间  fishNum:钓到的鱼数量 
		for(int t = 1; t <= endT-st[i]; ++t)
		{
			if(pq.empty())
				break;
			int mxi = pq.top();//当前每分钟钓鱼数量最大的鱼塘编号 
			pq.pop();
			fishNum += f[mxi];//总钓鱼数量增加f[mxi]
			f[mxi] -= de[mxi];//该鱼塘每分钟钓鱼数量减少
			if(f[mxi] > 0)//如果在该鱼塘还能钓鱼 
				pq.push(mxi); //把该鱼塘加入到优先队列 
		}
		mxFish = max(mxFish, fishNum);//更新最大钓鱼数量 
	}
	cout << mxFish;
	return 0;
}
相关推荐
明月看潮生14 分钟前
青少年编程与数学 02-003 Go语言网络编程 15课题、Go语言URL编程
开发语言·网络·青少年编程·golang·编程与数学
明月看潮生19 分钟前
青少年编程与数学 02-003 Go语言网络编程 14课题、Go语言Udp编程
青少年编程·golang·网络编程·编程与数学
小刘|23 分钟前
《Java 实现希尔排序:原理剖析与代码详解》
java·算法·排序算法
南宫理的日知录25 分钟前
99、Python并发编程:多线程的问题、临界资源以及同步机制
开发语言·python·学习·编程学习
jjyangyou27 分钟前
物联网核心安全系列——物联网安全需求
物联网·算法·安全·嵌入式·产品经理·硬件·产品设计
逊嘘42 分钟前
【Java语言】抽象类与接口
java·开发语言·jvm
van叶~44 分钟前
算法妙妙屋-------1.递归的深邃回响:二叉树的奇妙剪枝
c++·算法
Half-up44 分钟前
C语言心型代码解析
c语言·开发语言
简简单单做算法1 小时前
基于Retinex算法的图像去雾matlab仿真
算法·matlab·图像去雾·retinex
knighthood20011 小时前
解决:ros进行gazebo仿真,rviz没有显示传感器数据
c++·ubuntu·ros