题意:选的学分总和为n,并且学分为a的课有b种,总共有K(1<=k<=8)种学分不同的课,并且要选的学分最多为40;问选课方案有多少种?(学分相同的课即认为相同)
解法1:朴素背包 求解不重复子结构问题
原本认为只要在完全背包和01背包中把循环do式改成f[v]+=f[v-w]即可,但是发现很多重叠部分;给组小数据就会发现了;
1
3 3
1 2
2 3
3 3
原本f[1]只有1中,但是在二进制中,开始k = 1,f[1] += f[0],f[1]就等于1了,之后循环外又一次num = 1使得f[1]两次加了f[0];得到的f[1]为2,显然这里面是有重复的;为什么在这种情况下会是有重复呢?前面我们求的是利用比较得到局部最优解, 但是现在局部最优解不能重合,就直接导致了二进制的优化(优化是认为可以有重叠的个数)是错误的;
这样就直接确定v用w乘以可以的个数累加即可;
#include<bits/stdc++.h> using namespace std; #define rep0(i,l,r) for(int i = (l);i < (r);i++) #define rep1(i,l,r) for(int i = (l);i <= (r);i++) #define rep_0(i,r,l) for(int i = (r);i > (l);i--) #define rep_1(i,r,l) for(int i = (r);i >= (l);i--) #define MS0(a) memset(a,0,sizeof(a)) #define MS1(a) memset(a,-1,sizeof(a)) #define inf 0x3f3f3f3f #define lson l, m, rt << 1 #define rson m+1, r, rt << 1|1 typedef __int64 ll; template<typename T> void read1(T &m) { T 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();} m = x*f; } template<typename T> void read2(T &a,T &b){read1(a);read1(b);} template<typename T> void read3(T &a,T &b,T &c){read1(a);read1(b);read1(c);} template<typename T> void out(T a) { if(a>9) out(a/10); putchar(a%10+‘0‘); } int f[44],V; void MultiPack(int w,int num) { for(int v = V;v >= w;v--){ for(int k = 1;k <= num && k*w <= v;k++) f[v] += f[v-k*w]; } } int w[10],num[10]; int main() { int T,n,k; read1(T); while(T--){ MS0(f);f[0] = 1; read2(V,k); rep1(i,1,k) read2(w[i],num[i]); rep1(i,1,k) MultiPack(w[i],num[i]); out(f[V]); puts(""); } return 0; }
原文:http://www.cnblogs.com/hxer/p/5202376.html