背包九讲 之 01背包求方案数

01背包求方案数

本文基于01背包问题

问题重述

有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。第 i 件物品的体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。输出 最优选法的方案数。注意答案可能很大,请输出答案模 109+7 的结果。

输入格式:
第一行两个整数,N,V,用空格隔开,分别表示物品数量和背包容积。
接下来有 N 行,每行两个整数 vi,wi,用空格隔开,分别表示第 i 件物品的体积和价值。

输出格式:
输出一个整数,表示 方案数 模 109+7 的结果。

数据范围
0<N,V≤1000
0<vi,wi≤1000

思路分析:

分析过01背包后这道题便也容易解决,我们知道01背包的状态转移方程是 f[ i ][ j ] = max( f[ i -1][ j ] , f[ i-1 ][ j -v[i]]+wi ) 如果我们能记录转移到某种状态的方案数,我们便可求最终的方案数。只需要加一个数组记录每一种状态的方案数,然后加一个判断,跟着状态转移就可。

即如果 f[ i ][ j ] 是由 f[ i -1][ j ]f[ i -1][ j -v[i]]+wi 其中一种状态转移过来的那么它对应的状态方案数就是,转移过来的那个即 counts[ i ][ j ] = counts[ i -1][ j ] 或是 counts[ i ][ j ] =counts[ i -1][ j -v[i]] 如果两者都能转移到当前状态那么当前状态的方案数就是两者之和,即 counts[ i ][ j ] = counts[ i -1][ j ] + counts[ i -1][ j -v[i]] 。 那么它对应的边界情况也比较清晰即 ij0counts[ i ][ j ]=1 因为此时 f[ i ][ j ]=0 所以对应的方案数量就是 1 就只有它本身,因为它是边界没有办法从它处转移过来。

当然在01背包问题中我们将状态转移方程最终优化到了 f[ j ] = max( f[ j ] , f[ j -v[i]]+wi ), 所以对应的我们用来记录方案的数组数组也可以对应降到一维,即 counts[ j ] = counts[ j ] 或是 counts[ j ] =counts[ j -v[i]] 或是 counts[ j ] = counts[ j ] + counts[ j -v[i]]

C++代码:
#include<iostream>
#include<vector>
using namespace std;
int main()
{
	int N,V;
	vector<int>ans,counts;
	cin>>N>>V;			    //录入物品数量及背包容积 
	ans.resize(V+10);		//将记录答案的数组开辟合适的空间一般多开几个 
	counts.resize(V+10,1);   //为记录方案的数组开辟合适的空间同时赋初值为1
	for(int i=0;i<N;i++)    //开始循环物品 
	{
		int volume,value;    
		cin>>volume>>value; //现场输入当前物品的体积和价值
		for(int j=V;j>=volume;j--) //开始从大到小遍历体积 
		{						   //因为优化到了一维转移问题变成了覆盖问题 
			if(ans[j] < ans[j-volume]+value) //如果当前值需要覆盖即覆盖两个数组相应值如果 
			{								 //ans[j] > ans[j-volume]+value 即不需覆盖因为一维的特性什么也不需做 
				ans[j]=ans[j-volume]+value;
				counts[j]=counts[j-volume];
			}
			else if(ans[j] == ans[j-volume]+value) //最特殊的当二者相同时ans[j]也不需覆盖
			{									   //但counts[j]要变成二者之和因为两种状态都能转移过来 
				counts[j]=(counts[j]+counts[j-volume])%1000000007;//对应题目取模 
			}	
		}
	}
	cout<<counts[V]<<endl; //输出结果 
	return 0;
}
  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值