洛谷P1717 钓鱼【DP+01背包变形】【绿】

Date:2022.03.17
题目描述
话说发源于小朋友精心设计的游戏被电脑组的童鞋们藐杀之后非常不爽,为了表示安慰和鼓励,VIP999 决定请他吃一次“年年大丰收”,为了表示诚意,他还决定亲自去钓鱼。
但是,因为还要准备 NOIP2013, z老师只给了他 H 个小时的空余时间,假设有 n 个鱼塘都在一条水平路边,从左边到右编号为 1, 2, 3 … n 。
VIP是个很讲究效率的孩子,他希望用这些时间钓到尽量多的鱼。他从湖1出发,向右走,有选择的在一些湖边停留一定的时间钓鱼,最后在某一个湖边结束钓鱼。他测出从第 i 个湖到 i+1个湖需要走 5×ti分钟的路,还测出在第 i 个湖边停留,第一个5分钟可以钓到鱼 fi,以后再每钓5分钟鱼,鱼量减少 di。为了简化问题,他假定没有其他人钓鱼,也不会有其他因素影响他钓到期望数量的鱼。请编程求出能钓最多鱼的数量。
输入格式
第一行:湖的数量n。
第二行:时间h(小时)。
第三行:n个数,f1…fn
第四行:n个数,d1…dn
第五行:n-1个数,t1…tn-1
输出格式
一个数,所能钓鱼的最大数量。
输入输出样例
输入 #1复制
2
1
10 1
2 5
2
输出 #1复制
31
说明/提示
1 <= H <= 16
2 <= n <= 25

思路:一眼01背包,二眼又觉得不是,开始写挂了。后来一想还是01背包,只不过像 a [ i − 1 ] 到 a [ i ] a[i-1]到a[i] a[i1]a[i]这一步跨越的需要单独讨论一下,这有这一步不在01背包范畴里。
讨论以分钟为单位,走一步是一分钟,不以五分钟为单位。用 m a x k [ i ] maxk[i] maxk[i]表示最多能在第 i i i个池塘里钓多少次(总不能钓到负数吧?)。
f [ i ] [ j ] : f[i][j]: f[i][j]:在第 i i i个池塘钓完鱼,已经过了 j j j分,最多能钓多少鱼。
状态转移方程:
f [ i ] [ j ] = f [ i − 1 ] [ j − t [ i − 1 ] ] ; f[i][j]=f[i-1][j-t[i-1]]; f[i][j]=f[i1][jt[i1]];【即只含从 i − 1 i-1 i1 i i i的花费,表示并未在第 i i i个池塘钓任何一条鱼】
f [ i ] [ j ] = m a x ( f [ i ] [ j ] , f [ i − 1 ] [ j − t [ i − 1 ] − k ] + a [ i ] ∗ k − d [ i ] ∗ k ∗ ( k − 1 ) / 2 ) ; 【 k ∈ [ 1 , m i n ( j , m a x k [ i ] ) ] 】 f[i][j]=max(f[i][j],f[i-1][j-t[i-1]-k]+a[i]*k-d[i]*k*(k-1)/2);【k\in[1,min(j,maxk[i])]】 f[i][j]=max(f[i][j],f[i1][jt[i1]k]+a[i]kd[i]k(k1)/2);k[1,min(j,maxk[i])]【即在第 i i i个池塘钓了 k k k次的方案数,求max】
此外,初始状态除了 f [ 1 ] [ 0 ] f[1][0] f[1][0]全不存在,置为-INF。答案不确定是什么状态钓鱼最多,遍历求个max。
代码如下:

#include 
using namespace std;
typedef long long LL;
const LL N = 1100,INF=0x3f3f3f3f3f3f3f3f,mod=1e9+7;
LL n,m,k,f[N][N],a[N],d[N],t[N],maxk[N];
int main()
{
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    cin>>n>>m;m*=12;
    for(int i=1;i<=n;i++) cin>>a[i];
    for(int i=1;i<=n;i++) cin>>d[i];
    for(int i=1;i<=n-1;i++) cin>>t[i];
    for(int i=1;i<=n;++i)
	    for(int j=0;j<=m;++j)
	    f[i][j]=-INF;
	f[1][0]=0;
    for(int i=1;i<=n;i++)
    {
        LL cnt=1,x=a[i],y=d[i];
        while(x-y>=0&&cnt<=m) {x-=y;cnt++;}
        maxk[i]=cnt;//cnt<=m是为了防止每次钓鱼后鱼会减0条,会死循环。
    }
    for(int i=1;i<=n;i++)
        for(int j=m;j>=t[i-1];j--)
        {
            f[i][j]=f[i-1][j-t[i-1]];
            for(int k=1;k<=min((LL)j,maxk[i]);k++)
            {
                if(j-t[i-1]-k<0) break;
                f[i][j]=max(f[i][j],f[i-1][j-t[i-1]-k]+a[i]*k-d[i]*k*(k-1)/2);
            }
        }
    LL ans=0;
    for(int i=1;i<=n;i++)
        for(int j=0;j<=m;j++)
            ans=max(ans,f[i][j]);
    cout<<ans;
    return 0;
}

你可能感兴趣的:(dp,动态规划,算法,c++)