原题链接:1262. 鱼塘钓鱼 - AcWing题库
有 N个鱼塘排成一排,每个鱼塘中有一定数量的鱼,例如:N=5 时,如下表:
鱼塘编号 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|
第1分钟能钓到的鱼的数量(1..1000) | 10 | 14 | 20 | 16 | 9 |
每钓鱼1分钟钓鱼数的减少量(1..100) | 2 | 4 | 6 | 5 | 3 |
当前鱼塘到下一个相邻鱼塘需要的时间(单位:分钟) | 3 | 5 | 4 | 4 |
即:在第 1 个鱼塘中钓鱼第 1 分钟内可钓到 10 条鱼,第 2分钟内只能钓到 8 条鱼,……,第 5 分钟以后再也钓不到鱼了。
从第 1 个鱼塘到第 2 个鱼塘需要 3 分钟,从第 2 个鱼塘到第 3 个鱼塘需要 5 分钟,……
给出一个截止时间 T,设计一个钓鱼方案,从第 1 个鱼塘出发,希望能钓到最多的鱼。
假设能钓到鱼的数量仅和已钓鱼的次数有关,且每次钓鱼的时间都是整数分钟。
输入格式
共 5 行,分别表示:
第 1 行为 N;
第 2 行为第1 分钟各个鱼塘能钓到的鱼的数量,每个数据之间用一空格隔开;
第 3 行为每过 1 分钟各个鱼塘钓鱼数的减少量,每个数据之间用一空格隔开;
第 4 行为当前鱼塘到下一个相邻鱼塘需要的时间;
第 5 行为截止时间 T。
输出格式
一个整数(不超过2^31−1),表示你的方案能钓到的最多的鱼。
数据范围
1≤N≤100 1≤T≤1000
输入样例:
代码语言:javascript复制5
10 14 20 16 9
2 4 6 5 3
3 5 4 4
14
输出样例:
代码语言:javascript复制76
暴力枚举法:
由于此题数据量很小可以进行暴力枚举,当前鱼塘可以掉一会时间,当到达转移时间时,可以花费c[i]去转移到下一个鱼塘,获得更多的鱼,dp[i][j]=dp[i 1][k] dp[i][j-k-c[i]];
代码语言:javascript复制#include<iostream>
#include<string>
using namespace std;
int n,t;
bool flag=0;
int a[105],a1[105],b[105],c[105];
int dp[105][1005];
int main(){
cin>>n;
for(int i=1;i<=n;i )cin>>a[i];
for(int i=1;i<=n;i )cin>>b[i];
for(int i=1;i<n;i )cin>>c[i];
cin>>t;
for(int i=1;i<=n;i ){
for(int j=1;j<=t;j ){
dp[i][j]=dp[i][j-1] a[i];
a[i]=max(a[i]-b[i],0);
}
}
for(int i=n-1;i>=1;i--){//逆序枚举鱼塘个数,因为下面要处理第i 1个鱼塘,i 1要先于i更新
for(int j=t;j>=c[i];j--){//j为总时间,花费最少c[i],用来走到下一个鱼塘
int maxx=dp[i][j];
for(int k=0;k<=j-c[i];k ){
//可以给i 1个分配k个时间,那么剩下j-k-c[i]为第i个鱼塘的时间
maxx=max(maxx,dp[i 1][k] dp[i][j-k-c[i]]);
}
dp[i][j]=maxx;
}
}
cout<<dp[1][t]<<endl;
return 0;
}
贪心:
这个题我们可以把一个鱼塘可以钓的鱼数全部列举出来,从中选取k大的数即为答案,这个k又是啥,我们的总时间T可以分为路程时间 钓鱼时间,我们枚举一下最多到哪一个终点,此时路程时间便可以知道,那么钓鱼的时间=总时间T-路程时间,注释附在代码上。
代码语言:javascript复制#include<iostream>
#include<cstring>
using namespace std;
const int N=105;
int n,t,res;
int a[N],b[N],c[N],s[N];
//a表示第 1分钟各个鱼塘能钓到的鱼的数量,b表示鱼减少的数量
//c表示到达各个鱼塘时间,s表示在第i个鱼塘钓的时间
int get(int k){//在第k个鱼塘可以钓的鱼数
return max(0,a[k]-b[k]*s[k]);//初始a[k],每次减少b[k],钓了s[k]分钟
//所以此时可以在第k个鱼塘钓a[k]-b[k]*s[k]个,但不能小于0
}
//多路归并,每一次寻找可以钓的最大鱼数
int work(int n,int t){//n表示最多移动到第n个鱼塘,t表示钓鱼的时间
int res=0;
memset(s,0,sizeof(s));//初始都为0
for(int i=1;i<=t;i ){//枚举每一分钟
int m=1;//记录最大下标
for(int j=2;j<=n;j ){//枚举1-n的鱼塘数
if(get(m)<get(j)){//第j个鱼塘所获得的鱼数小于第m个鱼塘所获得的鱼数,则更新下标
m=j;
}
}
res =get(m);
s[m] ;//在第m个上钓鱼要花费时间
}
return res;
}
int main(){
cin>>n;
for(int i=1;i<=n;i )cin>>a[i];
for(int i=1;i<=n;i )cin>>b[i];
for(int i=2;i<=n;i ){
cin>>c[i];
c[i] =c[i-1];//前缀和求花费移动鱼塘的时间
}
cin>>t;
for(int i=1;i<=n;i ){//枚举第一个鱼塘到第i个鱼塘最大获得多少鱼
res=max(res,work(i,t-c[i]));//t-c[i]表示总时间减去移动花费的时间,剩下为钓鱼的时间
}
cout<<res<<endl;
return 0;
}
这样时间复杂度会大大优化,如果数据量再大一点,暴力枚举就会寄了
此题贪心思路按照y总的讲解写的,里面涉及了多路归并等算法,此题还有更多解法,不再一一介绍了,文章尚有不足,欢迎大佬们指正。