题面:https://atcoder.jp/contests/abc145/tasks/abc145_e

题面估计都懂了,类似于背包问题。

首先,如果题目中没有给出 时间停止可以继续吃 这个条件的话,这个题就是一个裸的01背包问题。

然而,题目中加了这个条件,所以需要把背包问题转换一下:

之前背包的上限容量 是T,但是因为题目限制,所以可以把背包容量扩充到 T+w[i]-1

这个时候这个物品就可以超出 容量T ,并且小于 T+w[i] 也保证了不可能出现大于容量T还进行的情况。

所以状态转移方程:

应该都可以看懂

但是有一个坑点就是,wa了我好久,我们这样进行状态转移方程的时候,如果有重量大的先进行dp之后,就没办法继承前面重量小的状态,因为状态转移方程中:dp[x][y]与dp[x-1][y-w[i]]状态有关,所以要保证 y-w[i]这个状态存在并且是最优。所以我们应该按w从小到大排一下序,最后一遍O(n)即可。

AC:

#pragma GCC optimize(2)
#include <bits/stdc++.h>
#include<stdio.h>
#include <string.h>
#include<algorithm>
#include <queue>
typedef long long ll;
const int mod=998244353;
const int maxn=1e6+5;
const ll INF=100000000000000005;
const double eps=1e-11;
using namespace std;
ll n,m,p;
struct node{
    int w,v;
    bool friend operator<(node a,node b)
    {
        return a.w<b.w;
    }
}save[3005];
ll dp[3005][6005];
int main()
{
    scanf("%lld%lld",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%lld%lld",&save[i].w,&save[i].v);
    sort(save+1,save+1+n);
    for(int i=1;i<=n;i++)
    {
        for(int k=0;k<save[i].w+m;k++)
        {
            if(k>=save[i].w) dp[i][k]=max(dp[i-1][k],dp[i-1][k-save[i].w]+save[i].v);
            else dp[i][k]=dp[i-1][k];
        }
    }
    ll res=0;
    for(int i=1;i<=n;i++) res=max(dp[n][m+save[i].w-1],res);
    printf("%lld\n",res);
    return 0;
}