【题目描述】
在一条水平路边,有 n 个钓鱼湖,从左到右编号为 1,2,…,n。佳佳有 H个小时的空余时间,他希望利用这个时间钓到更多的鱼。他从 1 出发,向右走,有选择的在一些湖边停留一定的时间(是 5 分钟的倍数)钓鱼。最后在某一个湖边结束钓鱼。佳佳从第 i 个湖到第 i+1个湖需要走 5×Ti分钟路,还测出在第 i 个湖停留,第一个 5 分钟可以钓到 Fi条鱼,以后每再钓 5分钟,可以钓到的鱼量减少 Di ,若减少后的鱼量小于 0,则减少后的鱼量为 0 。为了简化问题,佳佳假定没有其他人钓鱼,也没有其他因素影响他钓到期望数量的鱼。请编程求出佳佳最多能钓鱼的数量。
【输入格式】
第一行一个整数 n,表示湖的个数
第二行一个整数 H,表示佳佳的空闲时间
第三行有 n 个整数,依次表示每个湖第一个 5分钟能钓到鱼的数量
第四行有 n 个整数,依次表示以后的每5分钟钓鱼数量比前一个 5 分钟钓鱼数量减少的数量
第五行有 n-1 个整数,Ti 表示由第 i 个湖到第 i+1个湖需要花 5*Ti分钟的路程
【输出格式】
输出只有一行,表示佳佳最多能钓鱼的数量。
【样例输入】
3
1
4 5 6
1 2 1
1 2
【样例输出】
35
【样例解释】
在第 11个湖钓 15分钟,共钓得 4+3+2=9条鱼;
在第 2 个湖钓 10 分钟,共钓得 5+3=8条鱼;
在第 3 个湖钓 20 分钟,共钓得 6+5+4+3=18 条鱼;
从第 1 个湖到第 2 个湖,从第 2 个湖到第 3 个湖,共用时间 15 分钟,共得 35 条鱼,并且这是最多的数量。
【数据范围与提示】
对于 100% 的数据,2≤n≤100,1≤H≤20。
思路:说实在的,这道题我没有用贪心,虽然是贪心,因为dp会更加好理解。所以我在这里解释dp,但是也会放上贪心的代码。重点就是要搞清楚他的dp含义是,dp[i][j]表示第一个湖到第i个湖用了j个单位时间,钓的最多鱼,要搞清楚他的每一次减少,使得总量形成了一个等差数列。大概就是这样,其他的看代码见识吧。
【代码一:dp】
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
inline int read()
{
char c=getchar();
int x=0,f=1;
while(c<48 || c>57)
{
if(c=='-') f=-1;
c=getchar();
}
while(c>=48 && c<=57)
{
x=x*10+c-48;
c=getchar();
}
return x*f;
}
int f[110],d[110];
int s[110],ss[110];
int dp[30][300];
//dp[i][j]表示第一个湖到第i个湖用了j个单位时间,钓的最多鱼
int main()
{
int n,h; n=read(); h=read(); h=h*12;
//假设5分钟为一个单位时间,那么一小时60分钟里面有12个单位时间
for(int i=1;i<=n;i++) f[i]=read();
for(int i=1;i<=n;i++) d[i]=read();
s[1]=0; ss[1]=0;//初始化没有用过任何时间
for(int i=2;i<=n;i++)
{
scanf("%d",&s[i]);
/*
从第i个湖走到i+1个用的时间
说白了有i-1个数,不就是从第一个湖开始
第一个数就是第一个湖到第二个湖走的时间
第二个数就是第二个湖到第三个湖走的时间
以此类推
*/
ss[i]=ss[i-1]+s[i];//ss[i]表示走路的时间
/*
因为我们把每个5分钟换成了单位时间
所以其实就是之间把s[i-1]+s[i]全部加起来
就是总共的走路时间
*/
}
memset(dp,0,sizeof(dp));//初始化
int ans=0;//记录数量
for(int i=1;i<=n;i++)//n个湖
{
for(int j=1;j<=h-ss[i];j++)
//h表示总时间,ss[i]表示走路的时间,h-ss[i]表示用在钓鱼的时间
{
dp[i][j]=dp[i][j-1];//保存一下之前的状态
//或:dp[i][j]=dp[i-1][j];
for(int k=0;k<=j;k++)//枚举在第i个池塘钓鱼的单位时间
if(f[i]-(k-1)*d[i]>0)
{
dp[i][j]=max(dp[i][j],k*(f[i]+f[i]-d[i]*(k-1))/2+dp[i-1][j-k]);
//这一步就是为了更新当前拥有的鱼
/*
k*(f[i]+f[i]-d[i]*(k-1))/2这是一个等差数列
f[i]是第一个单位时间也就是钓的最多的一次鱼
接下来就是k*f[i]减去(1*d[i]+2*d[i]+3*d[i]+...(k-1)*d[i])
也就是f[i]+f[i]-f[i]*(k-1)
(最多的首项减去最少的末项*k个单位时间)/2
就是当前这个湖可以钓的最多的鱼
dp[i-1][j-k]就是上一个湖钓的鱼
*/
}
}
ans=max(ans,dp[i][h-ss[i]]);//记录每一次湖的答案
}
printf("%d\n",ans);
return 0;
}
【贪心代码】
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
inline int read()
{
char c=getchar();
int x=0,f=1;
while(c<48 || c>57)
{
if(c=='-') f=-1;
c=getchar();
}
while(c>=48 && c<=57)
{
x=x*10+c-48;
c=getchar();
}
return x*f;
}
int t[110],f[110],d[110],a[110];
int main()
{
int n,h; n=read(); h=read(); h*=12;
int x;t[0]=0;
for(int i=1;i<=n;i++) a[i]=read();
for(int i=1;i<=n;i++) d[i]=read();
for(int i=1;i<n;i++)
{
x=read();
t[i]=t[i-1]+x;
}
int ans=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=i;j++) f[j]=a[j];
int kk=h-t[i-1]; int tt,maxx,sum=0;
for(int j=1;j<=kk;j++)
{
maxx=0;
for(int k=1;k<=i;k++)
if(f[k]>maxx) maxx=f[k],tt=k;
if(maxx==0)break;
f[tt]-=d[tt]; sum+=maxx;
}
ans=max(ans,sum);
}
printf("%d\n",ans);
return 0;
}