QBXT DP&图论班 2018.5.2 0/1背包专题

 P.S.:本文题目来自北京大学张浩威(张过亿)dalao的课件。

I.0/1背包

有n个物品,体积为m的背包,每个物品有一个价值vi,和体积ti,选
择若干物品,使得体积之和不超过m的情况下价值之和最大。
n<=1000,m<=10000。

普通0/1背包。

II.0/1背包+

n<=1000,vi<=10。

按照记忆化搜索的思路:

设x=当前物品编号,y=当前体积之和,z=当前价值之和

当x与z固定时,y越小越好。因此将x与z作为状态,将y作为dp的值。

O(nvi)

III.0/1背包++

n<=40

 这道题和DP没什么关系。正解是折半搜索.

O(2^(n/2)*n)  折半搜索  所有物品分成两半,对两半分别进行搜索,一种方案 <==> 两边各取一个拼起来

IV.0/1背包+++

价值之和模p最大

n<=40 p<=10000

因为 (a+b+c)%p <==> ((a+b)%p+c)%p

因此价值vi<=10000 同时

O(np)

V.0/1背包++++

对于任意第i个物品,求第i个物品一定不在背包时的最大价值。
n<=1000,m<=10000。

一个物品k不在背包时的最大价值,相当于将最大价值分为1~k-1的最大价值与k+1~n的最大价值。

我们可以O(nm)的算出一个前缀的最大价值和一个后缀的最大价值。

然后枚举以下分割的体积,算出ans。

前i个物品,体积之和是j,价值之和最大是多少
一个前缀的物品集合
后缀的物品集合

假如第x个物品不在了,1~x-1  +  x+1~n  一段和一段后缀。
它们分别被分配了多少体积。
枚举一下

枚举x,再枚举分配给了前缀多少体积,求max
f[i][j]  1~i 体积为j最大价值
g[i][j]  i~n  体积为j最大价值

for (x=1; x<=n; x++)
{
  ans=0;
  for (j=0; j<=m; j++)
    ans=max(ans,f[x-1][j]+g[x+1][m-j]);
  cout<<ans<<endl;
}

VI.0/1背包+++++

输出方案
• n<=10000,m<=10000。
• TL=2s
• ML=1M

一般0/1背包的输出方案

for (i=1; i<=n; i++)
{
  for (j=0; j<=m; j++)
  {
    dp[i][j]=dp[i-1][j];
    g[i][j]=j;
    if (j>=w[i])
      if (dp[i-1][j-w[i]]+v[i]>dp[i][j])
      {
        dp[i][j]=dp[i-1][j-w[i]]+v[i];
        g[i][j]=j-w[i];
      }
  }
}  // g[i][j] : dp[i][j] 由 dp[i-1][g[i][j]]转移得到
j=m;
for (i=n; i>=1; i--)
{
  if (j!=g[i][j]) 第i个物品被选择了
  j=g[i][j];
} 

然而本题非常良心的把内存设为1M,也就是说我们不能开二维数组,即只能用一维数组来存方案。

此处zhw介绍了一种精妙的做法:

g[i]表示dp[i][当前体积v]由dp[i/2][g[i]]转移得到,然后不断分治。

最终分割的矩形越来越小,最终形成一条从1到n的路径。

这样就在O(M)的空间复杂度内求出了最优方案。

猜你喜欢

转载自www.cnblogs.com/Loi-Brilliant/p/8983220.html