Comet OJ - Contest #11 B usiness 背包预处理+背包状态新形式+完全背包

题意:你要计算如下模型能得到的最大钱数:

有 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\n", ans);
}

猜你喜欢

转载自www.cnblogs.com/-Zzz-/p/11588135.html