0.注意!在学习背包计数问题前最好先学习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--;