P1064 [NOIP2006 提高组] 金明的预算方案
考察:分组背包dp
错误思路:
设置f[i][j]前i组选j个的集合,但这样划分集合没有考虑钱数,求不到最优解.pass
设置f[i][j][k]表示前i组选j个体积为k的三维数组,先不说有MLE的风险,而且枚举几个的时候很难计算体积
正确思路:
由上面应该要联想到状压dp,在能枚举出几个的情况下,还能计算相应的体积与价值.将每个无父节点的结点分别看成一组,因为子节点很少所以可以用二进制枚举选择的情况.每组的每个情况都是互斥,最后融合的体积为m,因此是分组背包模型.
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <vector> 5 using namespace std; 6 const int N = 32010,M = 65; 7 int n,m,v[M],w[M],f[N]; 8 vector<int> b[M],tmp[M],val[M]; 9 void init() 10 { 11 for(int i=0;i<b[0].size();i++) 12 { 13 int sz = b[b[0][i]].size(),t =b[0][i]; 14 tmp[i+1].push_back(0); 15 val[i+1].push_back(0); 16 for(int j=0;j<1<<sz;j++) 17 { 18 int res = v[t]*w[t],price = v[t]; 19 for(int k=0;k<sz;k++) 20 if(j>>k&1) 21 { 22 res+=v[b[t][k]]*w[b[t][k]]; 23 price+=v[b[t][k]]; 24 } 25 tmp[i+1].push_back(res); 26 val[i+1].push_back(price); 27 } 28 } 29 } 30 int main() 31 { 32 scanf("%d%d",&m,&n); 33 for(int i=1;i<=n;i++) 34 { 35 int q; 36 scanf("%d%d%d",&v[i],&w[i],&q); 37 b[q].push_back(i); 38 } 39 init(); 40 for(int i=1;i<=b[0].size();i++) 41 for(int j=m;j>=0;j--) 42 for(int k=0;k<tmp[i].size();k++) 43 if(j>=val[i][k]) f[j] = max(f[j],f[j-val[i][k]]+tmp[i][k]); 44 printf("%d\n",f[m]); 45 return 0; 46 }
以上只适用于子节点很少的情况,一旦子节点个数多了就会TLE到难以直视.这道题本质是求选父节点和不选父节点的最大值.在已经确定选择父节点的情况下,可以将二进制枚举方案数换成01背包,这样时间复杂度就从O(2m)变成O(m).
先计算只有父节点的情况,在此情况下计算01背包,最后将选父节点与不选父节点进行比较.(一定要计算完父子节点01背包的情况再与选不选父节点比较)
1 #include <iostream> 2 #include <cstring> 3 #include <vector> 4 using namespace std; 5 const int N = 32010,M = 61; 6 typedef pair<int,int> PII; 7 int w[N],v[N],n,m,f[M][N]; 8 vector<PII> son[M]; 9 PII fa[M]; 10 int main() 11 { 12 scanf("%d%d",&m,&n); 13 for(int i=1;i<=n;i++) 14 { 15 int p; 16 scanf("%d%d%d",&v[i],&w[i],&p); 17 if(p) son[p].push_back({v[i],v[i]*w[i]}); 18 else fa[i] = {v[i],v[i]*w[i]}; 19 } 20 for(int i=1;i<=n;i++) 21 { 22 if(fa[i].first) 23 { 24 for(int j=fa[i].first;j<=m;j++)//加上父节点 25 f[i][j] = f[i-1][j-fa[i].first]+fa[i].second; 26 for(int k=0;k<son[i].size();k++) 27 for(int q = m;q>=son[i][k].first;q--) 28 if(f[i][q-son[i][k].first]) 29 f[i][q] = max(f[i][q-son[i][k].first]+son[i][k].second,f[i][q]); 30 } 31 for(int j=0;j<=m;j++)//没选父节点传递下去. 32 f[i][j] = max(f[i-1][j],f[i][j]); 33 } 34 printf("%d\n",f[n][m]); 35 return 0; 36 }