0-1背包

0-1背包的搜索写法:
int n,w[maxn],v[maxn],ans,limit;
void dfs(int i,int noww,int nowv)
{
   if(i>n)
   {
       if(noww<=limit)
           ans=max(anx,nowv);
       return;
   }
   dfs(i+1,noww+w[i+1],nowv+v[i+1]);
   dfs(i+1,noww,nowv);
}
cin>>n>>limit;
for(int i=1;i<=n;++i)
   cin>>w[i];
for(....)
   cin>>v[i];
dfs(0,0,0);
cout<<ans<<endl;
 
这种写法是超时的,要用
1.记忆化搜索+剪枝
2.dp递推式
以上两种方法也是动态规划的两种基本方法:
动态规划的数据形式:dp[maxn][maxn]或者dp[maxn](即一维数组或者二维数组)
关于dp的容量问题,容量的大小取决于有多少钟物品可供选择以及背包的容量,所以大小为N*V,所以可以设置一个二维数组或者一维数组,其中dp[i][j]表示放置第i个物品时的容量是j的的时候,现有的价值为多少。
之后,要考虑状态转移方程。 

0-1背包的递推式写法:
int n,v,c[maxn],w[maxn];
for(int i=1;i<=n;++i)
{
     for(int j=0;j<=v;++j)
     {
         if(j>=c[i])   dp[i][j]=max(dp[i-1][j],dp[i-1][j-c[i]]+w[i]);
         else   dp[i][j]=dp[i-1][j];
     }
}
cout<<dp[n][v]<<endl; 
关于递推式的说明:
1.首先,dp递推式的二维数组的容量是n*w,所谓的状态转移也就是考虑背包的第i个物品放或者不放的问题,所谓的考虑也就是比较那一个大,但是这样说也是不准确的,所谓的放与不放:
不放:这个时候的容量就是第dp[i-1][j]时候的容量,
放:这个时候的容量就是想在使得物品的容量<=j时(实际表现为下标为j),我要把第i个物品放上去。
这样,在数学表达式上,就是dp[i-1][j-c[i]],
至于,为什么是j-c[i],
首先,我们考虑这样一个例子:

N=5,V=10;
5 10
Ci:5 4 3 2 1
Wi:1 2 3 4 5
我们用表格来记录当前的dp[i][j]:
j: 1 2 3 4 5  6 7 8 9 10  
i=1;0 0 0 0 1 1 1 1 1 1
i=2:0 0 0 2 2 2 2 2 3 3
i=3:0 0 3 3 3 3 5 5 5 5
i=4:0 4 4 4 7 7 7 7 9 9
i=5:5 5 9 9 9 12 12 12 12 14
观察到,对每一行,dp[i][j]都在递增或者不变,
所以,对于放置第i个物品,有0~j-c[i]中选择,又因为时递增地,并且所有的选择都不会导致背包的容量超限,所以,我们本着贪心的思想,选择最大的哪一种,就是j-c[i]下的dp[i-1][j*]=dp[i-1][j-c[i]]
为什么要加上w[i],因为是减去了c[i],相当于减去了相应的wi*(包含了被舍弃的其他物品的价值之和),而又因为我们放置了第i个物品,所以要加上w[i],来表示dp[i-1][j-c[i]]的更新的值。
8月6日, 2018, 10:59
第二个递推式的说明:
for(int i=0;i<n;++i)
{
     for(int j=0;j<=v;++j)  
     {
          if(j+c[i+1]<=v)
               dp[i+1][j+c[i]]=max(dp[i+1][j+c[i]],dp[i][j]+w[i]);
          else
               dp[i+1][j]=max(dp[i+1][j],dp[i][j]);
     }
}

 

1.注意到,i是从0开始的,到n-1结束,j和原来一样从0开始。
2.if语句起到dp第一个递推式的if作用,用来筛选放与不放的最大值,else的作用起到保护作用,因为如果没有else的max操作,而且第一行都是0,就会使得下面全是0。没有筛选的作用。
3.if里面,是从j+c[i]开始填充的,就是说从(v[i+1]~v)范围开始状态转移。以前的数全都是0.
0-1背包状态转移方程的实质是:
{放得下:{放:-----}max(放,不放)
{       {不放:----}
{放不下:等同于上一个容量的价值。//注意保护操作
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值