题目大意:一个公主有一个摆满瓷器的架子,她生气的时候就要打碎m个瓷器。这个架子有n层,每层的瓷器每次只能从最左边拿或者从最右边拿,问打碎的瓷器的最大价值。
题解:这是一个泛化物品+分组背包的DP,首先将每一层上拿出瓷器的方案作为一个物品,拿出瓷器的方案的代价是瓷器数量,价值是这一层上所有方案中最大的价值。
首先为了计算方案的时候可以快速计算,我们在读入的时候就计算出前缀和sum,然后在计算方案的时候,每一层上打碎的数量对应一个物品,然后枚举总数量分配到两侧打碎的价格,得到最大价值。最后使用分组背包解决。
分组背包常见代码:
K代表有K个组,V是背包最大容量,这里要注意在最里面要防止v-ci小于0,否则会出现未定义行为,导致计算错误
for k 1 to K
for v V to 0
for all item i in group k
F[v]=max(F [v], F [v - Ci] + Wi);
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstring> 5 #include<vector> 6 using namespace std; 7 struct datatype 8 { 9 int value; 10 int space; 11 }; 12 vector<datatype> a[101]; 13 int shelf[101][101]; 14 int sum[101][101]; 15 int amount[101]; 16 int f[10001]; 17 int main() 18 { 19 int n,m; 20 scanf("%d%d",&n,&m); 21 memset(sum,0,sizeof(sum)); 22 for(int i=1;i<=n;i++) 23 { 24 int x; 25 scanf("%d",&x); 26 amount[i]=x; 27 for(int j=1;j<=x;j++) 28 { 29 scanf("%d",&shelf[i][j]); 30 sum[i][j]=sum[i][j-1]+shelf[i][j]; 31 } 32 } 33 for(int i=1;i<=n;i++) 34 { 35 for(int k=1;k<=amount[i];k++) 36 { 37 datatype tmp; 38 tmp.space=k; 39 tmp.value=0; 40 for(int j=0;j<=k;j++) 41 { 42 int tmp_v=0; 43 tmp_v+=sum[i][j]; 44 tmp_v+=sum[i][amount[i]]-sum[i][amount[i]-(k-j)]; 45 tmp.value=max(tmp_v,tmp.value); 46 } 47 a[i].push_back(tmp); 48 } 49 } 50 memset(f,0,sizeof(f)); 51 for(int i=1;i<=n;i++) 52 { 53 for(int j=m;j>=0;j--) 54 { 55 vector<datatype>::iterator it; 56 for(it=a[i].begin();it!=a[i].end();it++) 57 { 58 if(j-(*it).space<0) 59 { 60 continue; 61 } 62 f[j]=max(f[j],f[j-(*it).space]+(*it).value); 63 } 64 } 65 } 66 printf("%d\n",f[m]); 67 return 0; 68 }
时间: 2024-10-31 22:57:57