————————————————————————————————————————
这题有很多方法
60%是将每一种v[i]进行计算
100% 我的方法是先做一个01背包,让后每次减去v[i]的贡献,计算答案,空间O(n)时间O(nm);
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int M=10005,N=1005,P=1e9+7; int f[M],v[N],ans[N]; int n,m; int main() { freopen("bag.in","r",stdin); freopen("bag.out","w",stdout); scanf("%d%d",&n,&m); f[0]=1; for (int i=1;i<=n;i++) scanf("%d",&v[i]); for (int i=1;i<=n;i++) { if (!v[i]) continue; for (int j=m;j>=v[i];j--) f[j]=(f[j]+f[j-v[i]])%P; } for (int i=n;i>=1;i--) { if (!v[i]) {ans[i]=f[m];continue;} for (int j=v[i];j<=m;j++) f[j]=(f[j]-f[j-v[i]]+P)%P; ans[i]=f[m]; for (int j=m;j>=v[i];j--) f[j]=(f[j]+f[j-v[i]])%P; } for (int i=1;i<=n;i++) printf("%d\n",ans[i]); }
还有前后分别做一次01背包,将f[i-1],g[i+1]合并,空间O(nm)时间O(nm);
背包合并一开始不会,但其实很简单,将能组合成m的运用乘法原理进行合并 0与m ,1与m-1……
/*F1:#include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int M=10005,N=1005,P=1e9+7; int f[M<<1],v[N],ans[N]; int n,m; int main() { freopen("bag.in","r",stdin); freopen("bag.out","w",stdout); scanf("%d%d",&n,&m); f[0]=1; for (int i=1;i<=n;i++) scanf("%d",&v[i]); for (int i=1;i<=n;i++) { for (int j=m;j>=v[i];j--) f[j]=(f[j]+f[j-v[i]])%P; } for (int i=n;i>=1;i--) { for (int j=v[i];j<=m;j++) f[j]=(f[j]-f[j-v[i]]+P)%P; ans[i]=f[m]; for (int j=m;j>=v[i];j--) f[j]=(f[j]+f[j-v[i]])%P; } for (int i=1;i<=n;i++) printf("%d\n",ans[i]); }*/ #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int M=10005,N=1005,P=1e9+7; int f[N][M],g[N][M],v[N]; int n,m; int main() { freopen("bag.in","r",stdin); freopen("bag.out","w",stdout); scanf("%d%d",&n,&m); for (int i=1;i<=n;i++) scanf("%d",&v[i]); f[0][0]=1; for (int i=1;i<=n;i++) { for (int j=m;j>=v[i];j--) f[i][j]=(f[i-1][j]+f[i-1][j-v[i]])%P; for (int j=0;j<v[i];j++) f[i][j]=f[i-1][j]; } g[n+1][0]=1; for (int i=n;i>=1;i--) { for (int j=m;j>=v[i];j--) g[i][j]=(g[i+1][j]+g[i+1][j-v[i]])%P; for (int j=0;j<v[i];j++) g[i][j]=g[i+1][j]; } for (int i=1;i<=n;i++) { int ans=0; for (int j=0;j<=m;j++) ans=(ans+(long long)f[i-1][j]*g[i+1][m-j])%P; printf("%d\n",ans); } }
还有容斥,最为玄学。对于一个v[i],他会因为和其他v组合而形成不同的贡献,我们要做的是将贡献剔出来,
for (int j=1;j*v[i]<=m;j++) { if (j&1) ans=(ans-f[m-j*v[i]]+P)%P; else ans=(ans+f[m-j*v[i]])%P;}
f[m-1*v[i]]包含f[m-2*v[i]],f[m-2*v[i]]包含f[m-3*v[i]]……还有f[m-0*m]
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int M=10005,N=1005,P=1e9+7; int f[M<<1],v[N],ans[N]; int n,m; int main() { freopen("bag.in","r",stdin); freopen("bag.out","w",stdout); scanf("%d%d",&n,&m); f[0]=1; for (int i=1;i<=n;i++) scanf("%d",&v[i]); for (int i=1;i<=n;i++) { for (int j=m;j>=v[i];j--) f[j]=(f[j]+f[j-v[i]])%P; } for (int i=1;i<=n;i++) { int ans=f[m]; for (int j=1;j*v[i]<=m;j++) { if (j&1) ans=(ans-f[m-j*v[i]]+P)%P; else ans=(ans+f[m-j*v[i]])%P; } printf("%d\n",ans); } }