AcWing.11 背包问题求方案数(二维->一维)

AcWing.11 背包问题求方案数
问题描述

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

输入格式

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

输出格式

输出一个整数,表示 方案数模 1e9+7 的结果。

数据范围

0 0

输入样例
4 5
1 2
2 4
3 4
4 6
输出样例
2
分析

本题求01背包的最佳方案数,那么定义两个数组:dp和count
dp用来存储背包容积为j时的最佳方案的总价值,count用来存储与dp对应的最优方案数量。

先初始化count数组为1,因为在没有物品装入时最优方案就是一种,即什么都不放的。
再根据状态转移方程,选取状态转移方程dp[i][j] =max(dp[i-1][j],dp[i-1][j-v]+w),哪个最大就使用对应的count。如何一样大,说明两种方案均可,则把两种方案数量都加起来。
如下。

if(dp[i-1][j]>dp[i-1][j-v]+w) count[i][j] = count[i-1][j];
if(dp[i-1][j]<dp[i-1][j-v]+w) count[i][j] = count[i-1][j-v];
if(dp[i-1][j]<dp[i-1][j-v]+w) count[i][j] = (count[i-1][j]+count[i-1][j-v]);
C++代码
二维
#include
using namespace std;

const int maxn = 1010;
const int mod = 1e9+7;

//dp数组与对应的情况下的最优方案数 
int dp[maxn][maxn],count[maxn][maxn];
int main(){
	int N,V,v,w;
	
	cin>>N>>V;
	//i = 0时没有物品,故无论V多大最大价值为0,
	//故对应方案数为一个:什么都不放 
	for(int i = 0;i<=V;i++){
		count[0][i] = 1;
	}
	
	for(int i = 1;i<=N;i++){
		//第i个物品的体积和价值 
		cin>>v>>w;
		
		//这里正着遍历和倒着遍历均可(因为是二维的) 
		for(int j = V;j>=0;j--){
			//背包容量小于物品体积
			if(j<v) { 
				//于是第i个物品最优方案以及最优值和第i-1个相同 
				count[i][j] = count[i-1][j];
				dp[i][j] = dp[i-1][j];
			}
			else {
				if(dp[i-1][j-v]+w>dp[i-1][j]) //选择第i个物品(只有唯一方案) 
					count[i][j] = count[i-1][j-v]; 
			    else if(dp[i-1][j-v]+w==dp[i-1][j])//选不选都可以(两种方案均可) 
					count[i][j] = (count[i-1][j]+count[i-1][j-v])%mod;
				else //不选择第i个物品 (只有唯一方案)
					count[i][j] = count[i-1][j];
				//状态转移方程 
				dp[i][j] = max(dp[i-1][j],dp[i-1][j-v]+w);
			}
		}		
	}
	cout<<count[N][V]<<endl;
	return 0;
} 
一维

因为只用到i和i-1,我们降成一维。

#include
 
using namespace std;

const int maxn = 1010;
const int mod = 1e9+7;
//dp数组与对应的情况下的最优方案数 
int dp[maxn],count[maxn];
int main(){
	int N,V,v,w;
	
	cin>>N>>V;
	
	//i = 0时没有物品,故无论V多大最大价值为0,
	//故对应方案数为一个:什么都不放 
	for(int i = 0;i<=V;i++){
		count[i] = 1;
	}
	for(int i = 1;i<=N;i++){
		cin>>v>>w;//第i个物品的体积和价值 
		
		//这里只能倒着遍历,这是由状态转移方程决定的
		//不懂的同学可以再去学习一下01背包 
		//同时,此处把二维中j和v大小关系的遍历直接放入到循环条件中,
		//在j
		for(int j = V;j>=v;j--){
			 
			if(dp[j-v]+w>dp[j]) //选择第i个物品(只有唯一方案) 
				count[j] = count[j-v];
			else if(dp[j-v]+w==dp[j])//选不选都可以(两种方案均可) 
				count[j] = (count[j]+count[j-v])%mod;	
			dp[j] = max(dp[j],dp[j-v]+w);//状态转移方程 
		}		
	}
	cout<<count[V]<<endl;
	return 0;
} 

你可能感兴趣的:(AcWing,算法,动态规划)