背包计数问题

0.注意!在学习背包计数问题前最好先学习01背包和完全背包

推荐   多重背包(内有01背包链接)

1.什么是背包计数问题?

  背包计数问题是指有一个体积为v的背包,有n个物体,每个物体的体积是wi,求有多少种方法把背包恰好装满。

2.背包问题的例题                                        

 例题

       2.1题目的大义

给定一个数组s,小A身上有v元,对于每一个在s中的数都只能选一次,要求出有多少种方法把钱花光。

       2.2DFS

 还是那句话,DFS是万能的(虽然一般不会AC),但实在不会时可以用它拿部分分。

代码很简单:

#include <bits/stdc++.h>//讨论区90TLE代码
using namespace std;
int a,b,sum,s[1002],m,n,to[105];
void search(int x,int z){
	if (a>=m){
		if (a==m)
		   sum++;
		return;
	}
	for (int i = z;i <= n;i++){
		a+=s[i];
		search(x+1,i+1);
		a-=s[i];
	}
}
int main(){
	cin>>n>>m;
	for (int i = 1;i <= n;i++)
		cin>>s[i];
	search(1,1);
	cout<<sum;
	return 0;
} 

神奇的是这题数据太水,拿了90pts,如果加上记忆化,那就AC了。

       2.3 状态转移方程

既然要用到dp,那肯定要有状态转移方程

1.选这道菜:

dp[j]=dp[j-w[i]]

2.不选这道菜

dp[j]=dp[j]

3.对于每道菜,可以选或不选,我们有方案数:

dp+=dp[j-w[i]]

即,原来的方案数加上选这道菜的方案数

      2.4 代码实现

这道题每种菜只能选一次(这餐馆也太低级了吧),所以要倒序枚举 。

#include<bits/stdc++.h>
using namespace std;
int n,m,a[102],dp[10002];
int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++)
		cin>>a[i];
	dp[0]=1;
    for(int i=1;i<=n;i++)
		for(int j=m;j>=a[i];j--)
			dp[j]=dp[j]+dp[j-a[i]];
	cout<<dp[m];
	return 0;
}

3.完全背包计数问题

      例题

    3.1代码实现

这道题与上一道题唯一的区别就是一个四次方数可以选择多次 ,所以要正序枚举

代码:

#include<bits/stdc++.h>
using namespace std;
int k[20];
int n,dp[100002];
int kp(int a){
	return a*a*a*a;
}
int main(){
	cin>>n;
	for(int i=1;i<=17;i++)
		k[i]=kp(i);
	memset(dp,0x3f,sizeof(dp));
	dp[0]=0;
	dp[1]=1;
	for(int i=1;i<=17;i++)
		for(int j=kp(i);j<=n;j++)
			dp[j]=min(dp[j],dp[j-kp(i)]+1);
	cout<<dp[n];
	return 0;
}

4.总结

     先学习01背包和完全背包做这种题会很轻松,除了状态转移方程不一样外,其他的代码还是一样的

祝你大赛时

unsigned long long rp=0;
rp--;

see you again 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值