knapsack=背包(英语matters a lot)是的,没错,这道题就叫Knapsack(可蠢蠢的我即使知道是背包也无从下手,所以说思维matters most)
knapsack(和HDU 有点点差别,不过问题不大)
题目描述
有 n 个物品,第 i 个物品的重量为 ai 。
设 f(i,j,k,l,m) 为满足以下约束的物品集合数量:
1.集合中所有物品的重量和恰好为 m 。
2.集合包含物品 i 和物品 j 。
3.集合不包含物品 k 和物品 l 。
给出一个正整数 s ,求:
答案对 109+7 取模。
输入格式
第一行,两个正整数 n,s 。
第二行,n 个正整数 ai,描述每个物品的重量。
输出格式
输出一行,一个整数表示答案对 109+7 取模后的结果。
样例数据 1
输入
4 4
1 2 3 4
输出
8
备注
【数据规模与约定】
对于 30% 的数据,n,s≤10
对于另 20% 的数据,ai=1
对于 80% 的数据,n,s≤100
对于 100% 的数据,n,s≤1000
背包计数
其实很多的题目都有类似的特点,答案并不好求,但我们可以通过求出包含答案的一个东西,然后减去多余的部分(或是从中挑选我们需要的),就可以得到答案了,T3大概也是这样的。这也是正难求反思路的体现
下面上一个好写好理解的、但压线过(说不定哪一次就卡常数过不去了)的代码
用dp[i][j][s1][s2] (代码中是f来表示的) 来表示选前i个数,达到j的重量,且必选的数有s1个、必不选的数有s2个,的数量
那么转移的时候可以考虑,第i个物品是可选可 可不选的
dp[i][j][s1][s2]+=dp[i-1][j][s1][s2]+dp[i-1][j-a[i]][s1][s2]
或者第i个物品必选,或者必不选
dp[i][j][s1][s2]+=dp[i-1][j-a[i]][s1-1][s2]+dp[i-1][j][s1][s2-1](摘自感谢这位大佬)
(实现的话看代码吧,努力详细)
然后其实用不用滚动数组都可以,空间不会爆
#include<cstdio>//这个代码很玄。。最后一组数据刚好2000ms,压线过
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdlib>
using namespace std;
int n,s,a[2009];
long long f[3][2009][4][4];
const int mod=1e9+7;
int main(){
scanf("%d%d",&n,&s);
int i,j,k,s1,s2;
for(i=1;i<=n;++i){
scanf("%d",&a[i]);
}
bool sign=0;
f[0][0][0][0]=1;//对于选0个数,使其重量达到0,并且s1、s2都为0,就是什么都不做嘛,那什么都不做就只有1种可能啦
for(i=1;i<=n;++i){
sign=!sign;//滚动数组的体现
for(j=s;j>=0;--j)//必须逆序哦,否则会重复放
for(s1=0;s1<=2;++s1)//0表示没有一个数必选,1表示一个必选,2表示两个都必选
for(s2=0;s2<=2;++s2){
f[sign][j][s1][s2]=f[!sign][j][s1][s2]%mod;这个地方千万不能写成+=,因为用的是滚动数组,
///每次都是覆盖上去,如果是+=的话上次的结果会一直跟到最后
if(j-a[i]>=0){
f[sign][j][s1][s2]+=f[!sign][j-a[i]][s1][s2];
f[sign][j][s1][s2]%=mod;
if(s1>=1) f[sign][j][s1][s2]+=f[!sign][j-a[i]][s1-1][s2],f[sign][j][s1][s2]%=mod;
}
if(s2>=1) f[sign][j][s1][s2]+=f[!sign][j][s1][s2-1],f[sign][j][s1][s2]=f[sign][j][s1][s2]%mod;
}
}
long long ans=0;
for(i=1;i<=s;++i)
ans=(ans+f[sign][i][2][2])%mod;
ans=(ans*2)%mod*2%mod;//因为在s1、s2用0、1、2表示时,是默认了i<j,k<l所以要最终答案×4
printf("%I64d",ans);
return 0;
}
//这个代码很玄。。最后一组数据刚好2000ms,压线过
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdlib>
using namespace std;
int n,s,a[2009];
long long f[3][2009][4][4];
const int mod=1e9+7;
int main(){
scanf("%d%d",&n,&s);
int i,j,k,s1,s2;
for(i=1;i<=n;++i){
scanf("%d",&a[i]);
}
bool sign=0;
f[0][0][0][0]=1;//对于选0个数,使其重量达到0,并且s1、s2都为0,就是什么都不做嘛,那什么都不做就只有1种可能啦
for(i=1;i<=n;++i){
sign=!sign;//滚动数组的体现
for(j=s;j>=0;--j)//必须逆序哦,否则会重复放
for(s1=0;s1<=2;++s1)//0表示没有一个数必选,1表示一个必选,2表示两个都必选
for(s2=0;s2<=2;++s2){
f[sign][j][s1][s2]=f[!sign][j][s1][s2]%mod;这个地方千万不能写成+=,因为用的是滚动数组,
///每次都是覆盖上去,如果是+=的话上次的结果会一直跟到最后
if(j-a[i]>=0){
f[sign][j][s1][s2]+=f[!sign][j-a[i]][s1][s2];
f[sign][j][s1][s2]%=mod;
if(s1>=1) f[sign][j][s1][s2]+=f[!sign][j-a[i]][s1-1][s2],f[sign][j][s1][s2]%=mod;
}
if(s2>=1) f[sign][j][s1][s2]+=f[!sign][j][s1][s2-1],f[sign][j][s1][s2]=f[sign][j][s1][s2]%mod;
}
}
long long ans=0;
for(i=1;i<=s;++i)
ans=(ans+f[sign][i][2][2])%mod;
ans=(ans*2)%mod*2%mod;//因为在s1、s2用0、1、2表示时,是默认了i<j,k<l所以要最终答案×4
printf("%I64d",ans);
return 0;
}
下面再来一个,真正的正解(只要你搞明白了上面那个程序,这个就很好理解了,我就不赘述了)
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdlib>
using namespace std;
int n,s,a;
long long f[2009][4][4],g[2009][4][4];
const int mod=1e9+7;
void add(long long &a,long long b){//这个地方的&非常重要,相当于让主函数里的g数组也更改了
a+=b;
if(a>=mod) a-=mod;
}
int main(){
scanf("%d%d",&n,&s);
int i,j,k,s1,s2;
f[0][0][0]=1;
g[0][0][0]=1;
for(i=1;i<=n;++i){
scanf("%d",&a);
for(k=0;k<=s;++k)
for(s1=0;s1<=2;++s1)
for(s2=0;s2<=2;++s2)
{
if(f[k][s1][s2]){
if(k+a<=s){
add(g[k+a][s1][s2],f[k][s1][s2]);
if(s1<2) add(g[k+a][s1+1][s2],f[k][s1][s2]);
}
if(s2<2) add(g[k][s1][s2+1],f[k][s1][s2]);
}
}
memcpy(f,g,sizeof(f));
}
long long ans=0;
for(i=1;i<=s;++i)
{
ans=(ans+4*f[i][2][2])%mod;
}
printf("%I64d",ans);
return 0;
}
//这个地方的&非常重要,相当于让主函数里的g数组也更改了
a+=b;
if(a>=mod) a-=mod;
}
int main(){
scanf("%d%d",&n,&s);
int i,j,k,s1,s2;
f[0][0][0]=1;
g[0][0][0]=1;
for(i=1;i<=n;++i){
scanf("%d",&a);
for(k=0;k<=s;++k)
for(s1=0;s1<=2;++s1)
for(s2=0;s2<=2;++s2)
{
if(f[k][s1][s2]){
if(k+a<=s){
add(g[k+a][s1][s2],f[k][s1][s2]);
if(s1<2) add(g[k+a][s1+1][s2],f[k][s1][s2]);
}
if(s2<2) add(g[k][s1][s2+1],f[k][s1][s2]);
}
}
memcpy(f,g,sizeof(f));
}
long long ans=0;
for(i=1;i<=s;++i)
{
ans=(ans+4*f[i][2][2])%mod;
}
printf("%I64d",ans);
return 0;
}