01背包(DP)

01背包特点:

1.决策物品只有两种状态(选or不选)

2.决策物品的有权重(a[i])有上界P

3.求最大权值

4.一般物品数量较少(<100)

(注意变形)

解题:

 1.手工打表:

 PP-1 P-2.....
1(a[1] )a[1] +dp[j-a[1]a[1] ....
2(a[2] )max(a[2]+dp[j-a[2]],a[1])...............
3(a[3] )max(a[3]+dp[j-a[3],a[2]+dp[j-a[1],a[1]+dp[j-a[1])..................


 

  
 











dp[]值为表中所填(从左向右,从上向下)

 2.写转移方程  

        for(int i=0;i<z;i++)            //i为物品件数

        {
            for(int j=P;j-a[i]>=0;j--)
                dp[j]=max(dp[j],dp[j-a[i]]+a[i]);
        }





例题:1.Robberies 

            2.最大报销额 



1.Robberies 

直接做有点麻烦,可以转化为在大于逃脱率的情况下可以获得的最大钱数也就是求得到某个钱数时的最大逃脱率(1-被捕率),状态转移方程是 dp[j]=max(dp[j],dp[j-s[i].m]*s[i].p)
初值dp[0]=1 ,因为不偷钱当然是不被抓的,这里的dp[j]就是获得j钱数可以达到的最大逃脱率!几次抢劫必须同时逃脱才可以,因此概率要乘!这里有个常数级的优化,可以用sum[i] 记录前i的银行(包括i)的钱数和,这样第二个循环就可以是for(j = sum[i] ; j>=s[i].m;j--) , 因为当j >sum[i] 时 j-s[i].m>sum[i-1] (sum[i]-sum[i-1]=s[i].m)这时的dp[j-s[i].m]显然还没有算出来,因为此时最多只算到了sum[i-1] !

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=110;
int m[N],sum[N];
double p[N],dp[N*N];

int main()
{
    int t,n;
    double P;
    scanf("%d",&t);
    while(t--)
    {
        memset(dp,0,sizeof(dp));
        scanf("%lf%d",&P,&n);
        P=1-P;                //最大逃脱概率
        for(int i=0;i<n;i++)
        {
            scanf("%d%lf",&m[i],&p[i]);
            p[i]=1-p[i];
            sum[i]=(i==0?m[0]:sum[i-1]+m[i]);
        }
        dp[0]=1;
        for(int i=0;i<n;i++)
        {
            for(int j=sum[n-1];j>=m[i];j--)
            {
                dp[j]=max(dp[j],dp[j-m[i]]*p[i]);
            }
        }
        for(int j=sum[n-1];j>=0;j--)
        {
            if(dp[j]>P)
            {
                printf("%d\n",j);
                break;
            }
        }
    }
}



2.最大报销额 

整理出每张发票的总额,作为这件物品的价值也作为容量(相当于物品的体积),放到容量为q的背包中

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=35;
double dp[3000010];

int main()
{
    char type;
    int n,m,z,flag;
    double q,tpa,tpb,tpc,mon,all,per[N];
    while(~scanf("%lf%d",&q,&n)&&n)
    {
        z=0;
        memset(dp,0,sizeof(dp));
        for(int i=0;i<n;i++)              // i zhang stamp
        {
            tpa=0,tpb=0,tpc=0,all=0;
            flag=0;
            scanf("%d",&m);
            while(m--)                   //type
            {
                scanf("%*c%c:%lf",&type,&mon);
                all+=mon;
                if(type=='A')
                    tpa+=mon;
                else if(type=='B')
                    tpb+=mon;
                else if(type=='C')
                    tpc+=mon;
                else
                    flag=1;
            }
            if(tpa<=600&&tpb<=600&&tpc<=600&&all<=1000&&!flag)
                per[z++]=all*100;
        }
        int Q=(int)(q*100.0);
        for(int i=0;i<z;i++)
        {
            for(int j=Q;j-(int)per[i]>=0;j--)
                dp[j]=max(dp[j],dp[j-(int)per[i]]+(int)per[i]);
        }
        printf("%.2lf\n",dp[Q]*0.01);
    }
}





<think>嗯,用户问的是01背包DP定义是否可以定义为dp[i][j]示前i个物品,并且一定要取第i个物品,容量为j时的最大价值。这个问题需要仔细分析,因为传统的01背包定义通常没有“必须取第i个物品”的条件。我得先回想一下标准的01背包问题是如何定义的,然后再比较用户提出的这种定义是否可行,或者有什么不同之处。 首先,标准的01背包动态规划定义通常是dp[i][j]示前i个物品,容量为j的情况下能获得的最大价值,而这时候每个物品可以选择放或不放。状态转移方程是dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[i]] + v[i])。这种情况下,是否取第i个物品是可选的,所以这个状态转移方程考虑了两种情况。 而用户提出的定义是必须取第i个物品,那么这时候状态转移的方式就会不同。因为如果必须取第i个物品,那么对于前i个物品的情况,必须包含第i个物品的选择。此时,状态转移可能需要考虑前i-1个物品中,在容量j减去第i个物品重量的情况下的最大值,再加上第i个物品的价值。也就是说,可能的状态转移方程是dp[i][j] = dp[i-1][j - w[i]] + v[i],前提是j >= w[i]。否则的话,可能无法放入第i个物品,这种情况下dp[i][j]可能不存在,或者需要特殊处理。 但问题来了,当必须包含第i个物品时,前i个物品的选择实际上是否允许前面的物品有选择或不选择的情况?比如,当i=3时,是否必须选第三个,而前两个可以选择或不选?或者是否前i个物品都必须要选?这里可能用户的定义是前i个物品中必须选第i个,而前面的可以自由选择。如果是这样的话,那么这种定义下,最终的答案可能并不是传统意义上的全局最大值,因为必须包含最后一个物品的情况下,可能无法达到最优解,尤其是当最后一个物品很重或者价值不高的时候。 比如,假设总容量是5,物品列是三个物品,其中第三个物品的重量是6,这时候即使前两个物品的组合在容量5下能达到很高的价值,但第三个物品无法放入,那么dp[3][5]在这种情况下可能无法定义,或者必须设为负无穷之类的值,示不可能。这会导致在最终求解时需要遍历所有可能的i,找到最大的dp[i][j],而传统的方法只需要看dp[n][j]即可。 因此,用户提出的这种定义可能改变了问题的性质,使得最终解必须包含第i个物品,而i可能有多个可能的值。所以在这种情况下,如果要求最终解必须包含某个物品,那么可能需要遍历所有i的情况,取最大的那个。但通常情况下,01背包问题并没有这个限制,所以用户需要明确是否问题中存在必须包含某个物品的条件。 另外,这种定义方式可能会影响时间和空间复杂度。因为原来的状态转移方程允许每个状态由前一个状态转移而来,而这种新的定义可能需要更多的条件判断。例如,当计算dp[i][j]时,必须确保第i个物品被放入,因此只有当j >= w[i]时才能转移,否则这个状态可能无效。 再举个例子,比如物品i的重量是w[i],价值是v[i]。那么当处理到第i个物品时,必须装入它,那么只能从i-1个物品中选择,并且容量为j - w[i]时的最大价值加上v[i]。因此,状态转移方程应该是dp[i][j] = max(dp[i-1][j - w[i]] + v[i], ... )?或者是否有其他可能性? 但这里可能有疑问,因为如果必须选第i个物品,那么对于每个i来说,其状态只能由i-1的状态转移而来,并且必须加上当前物品的价值。这个时候,可能没有其他选择,比如不能选择不装第i个物品,所以状态转移方程可能不是max函数,而是直接继承之前的某个状态加上当前物品的价值。 但是这样的话,这样的dp数组是否能覆盖所有可能的情况呢?可能在某些情况下,比如当必须装入第i个物品时,但前面的物品并没有被装入,这可能允许不同的组合。例如,装入第i个物品,而前面的某些物品不装。这时候的转移可能需要考虑前i-1个物品中,容量为j - w[i]时的最大值,不管是否装入了第i-1个物品,只要容量足够就行。这可能和传统方法中的转移类似,但必须加上当前物品的价值。 但这样定义的话,最终的答案可能不是传统01背包的解,因为传统解法允许不选最后一个物品。因此,用户的问题在于是否这样的定义是可行的,或者是否有意义。 可能的结论是,这样的定义可以用于特定情况下的问题,比如必须选择第i个物品的情况,但一般情况下不适用。例如,当问题被修改为必须选择至少一个物品,或者必须包含某个特定物品时,这种定义可能有用。但作为一般的01背包问题,这样的定义可能无法得到正确的最优解,因为它强制包含第i个物品,而最优解可能并不包含它。 因此,用户需要明确问题的需求。如果题目确实有必须包含第i个物品的限制,那么这样的定义是合理的。否则,传统的定义更合适。同时,使用这种定义可能需要更多的处理步骤,比如最后需要遍历所有可能的i,取最大值,而传统方法只需要取dp[n][capacity]即可。 总结一下,用户的这种定义方式是有可能的,但适用于特定条件下的变种问题,而不是标准的01背包问题。需要根据具体的问题需求来决定是否采用这样的状态定义。</think>01背包问题的动态规划定义可以调整为$dp[i][j]$示**前$i$个物品中必须取第$i$个物品**且背包容量为$j$时的最大价值,但这种定义与传统01背包的语义和适用场景有所不同。以下是逐步分析: --- ### **1. 与传统定义的对比** - **传统定义**: $dp[i][j]$示前$i$个物品中**可选**取部分(不一定包含第$i$个物品),容量为$j$的最大价值。 状态转移方程: $$dp[i][j] = \max(dp[i-1][j], \ dp[i-1][j-w_i] + v_i)$$ 即第$i$个物品**可选**取或不取。 - **你的定义**: $dp[i][j]$强制要求**必须取第$i$个物品**,此时状态转移方程变为: $$dp[i][j] = \begin{cases} dp[i-1][j-w_i] + v_i, & \text{if } j \geq w_i \\ -\infty, & \text{otherwise} \end{cases}$$ 因为第$i$个物品必须被选取,所以只能从前$i-1$个物品的剩余容量中转移。 --- ### **2. 适用场景** 若问题要求**最终解必须包含第$i$个物品**(例如某些变种题目),你的定义是合理的。但在标准01背包问题中,这种定义会导致以下限制: - **最终答案需遍历所有$i$**: 传统方法直接取$dp[n][j]$即可,而你的定义需要计算$\max(dp[1][j], dp[2][j], \dots, dp[n][j])$,因为最优解可能不包含最后一个物品。 - **无法处理“不选第$i$个物品”的情况**: 若第$i$个物品重量过大($w_i > j$),则$dp[i][j]$无效。 --- ### **3. 实际应用示例** 假设物品列为$[(w_1, v_1), (w_2, v_2)]$,背包容量为$j$: - **传统方法**: $dp[2][j]$可以是$\max(\text{不选第2个}, \text{选第2个})$。 - **你的定义**: $dp[2][j]$必须选第2个物品,因此只能从$dp[1][j-w_2] + v_2$转移。 --- ### **4. 总结** - **可以定义**,但需明确问题是否需要**强制包含第$i$个物品**。 - **不推荐**用于标准01背包问题,因为传统定义更简洁且覆盖所有可能性。 - **适用变种问题**:例如“恰好选$k$个物品的背包问题”或“必须包含某物品的背包问题”。 --- ### **5. 代码实现差异(对比)** ```python # 传统01背包 for i in 1..n: for j in 0..capacity: dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[i]] + v[i]) # 你的定义 for i in 1..n: for j in 0..capacity: if j >= w[i]: dp[i][j] = dp[i-1][j-w[i]] + v[i] else: dp[i][j] = -infinity ``` 建议根据具体问题需求选择定义方式。若目标是标准01背包,传统定义更优。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值