完全背包——01背包方法数
这两天搞完01背包之后学习了完全背包这个完全背包是指物品数量无限,让自己来装考虑一下状态转移f[i][j]=max(f[i-1][j])尚未选择第i种物品,f[i][j]=max(f[i][j-w[i]+v[i]]);从第i键物品中选择一个显然状态是由不拿当前物品上一层的最优解和当前拿这个物品的最优解来进行比较从而进行转移。当然目标可以是maxf[n][j]或者是f[n][m]这两种都行只不过前者可以判断背包是否能被装满。
下面是二维的代码:
#include<ctime> #include<iostream> #include<cstdio> #include<algorithm> #include<cstdio> #include<iomanip> #include<map> #include<queue> #include<stack> #include<cstring> #include<string> #include<vector> using namespace std; inline long long read() { long long x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } const long long maxn=1001; long long f[maxn][maxn],w[maxn],v[maxn]; //显然f[i][j]表示前i种物品取j的容积所能达到的最优解。 long long n,m; int main() { // freopen("1.in","r",stdin); m=read();n=read(); for(long long i=1;i<=n;i++) { w[i]=read();v[i]=read(); } for(long long i=1;i<=n;i++) for(long long j=1;j<=m;j++) { f[i][j]=max(f[i][j-1],f[i-1][j]); if(j>=w[i])f[i][j]=max(f[i][j],f[i][j-w[i]]+v[i]); } printf("max=%lld\n",f[n][m]); return 0; }
代码正确,但是很容易爆空间。
而一维的完全背包是那样子的,把f[j]=max(f[j-w[i]]+v[i]);正序来选择因为可以在刚刚更新过的第i件物品之上再进行更新这样的话就意味着一件物品有无数件了。不算是很懵了
下面是一维的代码:
#include<ctime> #include<iostream> #include<cstdio> #include<algorithm> #include<cstdio> #include<iomanip> #include<map> #include<queue> #include<stack> #include<cstring> #include<string> #include<vector> using namespace std; inline int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } const int maxn=10001; int f[maxn],w[maxn],v[maxn]; int n,m; int main() { //freopen("1.in","r",stdin); m=read();n=read(); for(int i=1;i<=n;i++) { v[i]=read();w[i]=read(); } for(int i=1;i<=n;i++) { for(int j=w[i];j<=m;j++) { f[j]=max(f[j],f[j-w[i]]+v[i]); } } cout<<f[m]<<endl; return 0; }
值得一提的是在2018noip考试的时候day1 t2 就是一道简单的完全背包,可惜没看出来,错失一等后悔,爆搜有的时候不一定出奇迹,正解就是正解。
考试代码=爆搜+完全背包 正解:完全背包
考试代码:
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<string> #include<algorithm> #include<ctime> #include<iomanip> #include<vector> #include<map> #include<queue> #include<stack> using namespace std; inline int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } const int maxn=200; int t,n; int a[maxn],b[maxn],f[2502],vis[maxn],flag=0; void dfs(int x,int num) { if(flag==1)return; if(num==x) { if(flag==1)return; for(int k=1;k<=n;k++) { if(vis[k]==0) { memset(f,0,sizeof(f)); f[0]=1; for(int i=1;i<=x;i++) { for(int j=1;j<=a[k];j++) { if(j>=b[i])f[j]|=f[j-b[i]]; } } if(f[a[k]]!=1)return; } } flag=1;return; } for(int i=1;i<=n;i++) { if(flag==1)return; if(vis[i]!=1) { b[++num]=a[i]; vis[i]=1; dfs(x,num); num--; vis[i]=0; } } } int main() { freopen("money.in","r",stdin); freopen("money.out","w",stdout); t=read(); for(int u=1;u<=t;u++) { n=read();flag=0; for(int i=1;i<=n;i++)a[i]=read(); for(int i=1;i<n;i++) { memset(vis,0,sizeof(vis)); dfs(i,0); if(flag==1){printf("%d\n",i);break;} } if(flag!=1)printf("%d\n",n); } return 0; }
正解:
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<string> #include<algorithm> #include<ctime> #include<iomanip> #include<vector> #include<map> #include<queue> #include<stack> using namespace std; inline int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } const int maxn=200; int t,n; int a[maxn],f[25002],ans=0; int main() { //freopen("1.in","r",stdin); //freopen("money.out","w",stdout); t=read(); for(int u=1;u<=t;u++) { memset(f,0,sizeof(f)); n=read();f[0]=1;ans=0; for(int i=1;i<=n;i++)a[i]=read(); for(int i=1;i<=n;i++) for(int j=a[i];j<=25001;j++) f[j]+=f[j-a[i]]; for(int i=1;i<=n;i++)if(f[a[i]]==1)ans++; printf("%d\n",ans); } return 0; }
不灵活的下场很沉重!
又搞了一道01背包的方法数,这个还是比较简单的个人觉得方法数的话以下面的洛谷例题为例:
求出所有背包的数量,这样的话求助了学长,不会啊当时感觉特别困难做题没有举一反三的能力感觉自己还不行,灵性不够。这很显然是一道01背包的变形所以只要稍稍的把01背包的转移方程改一下即可,
f[j]=f[j]+f[j-w[i]];初始化f[0]=1;这样加上一些小细节这道题就可以被轻松a掉。
代码:
#include<ctime> #include<iostream> #include<cstdio> #include<algorithm> #include<cstdio> #include<iomanip> #include<map> #include<queue> #include<stack> #include<cstring> #include<string> #include<vector> using namespace std; inline int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } const int maxn=1002; int a[7]={0,1,2,3,5,10,20}; int w[maxn],f[maxn],n=0,m=0,ans=0; int main() { //freopen("1.in","r",stdin); for(int i=1;i<=6;i++) { int x; x=read(); for(int j=1;j<=x;j++) { w[++n]=a[i]; m+=a[i]; } } f[0]=1; for(int i=1;i<=n;i++) { for(int j=m;j>=w[i];j--) { f[j]=f[j]+f[j-w[i]];//从i个数中来选和为j的有多少种方案 } } for(int i=1;i<=m;i++) { if(f[i]!=0)ans++; } printf("Total=%d\n",ans); return 0; }