题意:你要计算如下模型能得到的最大钱数:
有 n 天,初始时你的钱数为 0,有 m 种可能操作,第 i 种会使你当前失去 ai 的钱数并在 n 天结束后返还 bi 的钱数。每一天可以执行任意多种操作,每种任意次(但每次操作后你的钱数不能为负)。每天结束时你会获得一个与当前持有钱数 x 相关的收入f(x) ,而 f(x) 单调不增。
分析:首先显然是动态规划,并且状态可分解为 前 i 天、当前持有钱数、最后能获得的投资回报。 选择较小的两个作为数组下标,最大的作为值。
那么 状态转移方程: 第 i 天获得津贴节点的过程转移为
dp[ i ][ j+w[ j ] ] = max( dp[ i ][ j+w[ j ] ], dp[ i-1 ][ j ] ) 就比如第二天的某个状态是由前一天的状态转移来的。
而对于存储 节点的过程,只需对每一种存储方式做一个完全背包即可。
注意:这个完全背包的状态是一个新的形式,众所周知,基础的背包问题的状态 j 都是指总容量,而本题的状态 j 是指手头上的钱,也就是剩余的容量。模拟现实理解一下就知道这个状态转移方程怎么写了。注意枚举方向。
注意:还没有转移到的状态为无效状态 , 需要置为很小的负数.
#include <bits/stdc++.h> using namespace std; const int inf = 0x3f3f3f3f; int a[105],b[105]; int w[1005]; int dp[105][1005+1005]; //表示的状态是第i天 手头上!剩余!的钱 (值)最多可以投资得到多少钱 int main(){ int n,m,k; scanf("%d %d %d",&n,&m,&k); for(int i=0; i<=k; i++){ scanf("%d",w+i); } for(int i=1; i<=m; i++){ scanf("%d %d",a+i,b+i); } for(int i=1; i<=n; i++){ for(int j=0; j<=2005; j++){ dp[i][j] = -inf; } } dp[1][0] = 0; for(int i=2; i<=n; i++){ for(int j=0; j<=1000; j++){ dp[i][ j+w[j] ] = max(dp[i][ j+w[j] ], dp[i-1][j]); } for(int k=1; k<=m; k++){ for(int j=1000; j>=0; j--){ dp[i][j] = max(dp[i][j], dp[i][ j+a[k] ]+b[k] ); //这里普通背包的j是指总容量,而这是指剩余的容量,也就是手头上的钱,所以模拟现实理解一下就知道状态转移方程怎么写了 } } } int ans=-1; for(int i=0; i<=1000; i++){ ans = max(ans, dp[n][i]+i+w[i]); } printf("%d ", ans); }