刚看完背包九讲就来做的这题,判断出是分组背包,一个砝码放在任意一个挂钩就是一个分组,其实就是比01背包多了一重循环,保证每个砝码只被放一次。陷入了分组背包的误区 错误地认为这题也和经典的背包问题一样是求最优方案或者最优方案总数。看了网上的题解才醒悟到具体问题要具体分析,从状态入手。这题是要得到恰好用完所有砝码并达到平衡态的方案数。而平衡态并不能通过找最优(即在状态转移中取较小或较大)来得到,因为不能保证在转移中取较大或较小就能使得最后一定能到达平衡态。
这里采用平衡度( ∑力臂 ∑ 力 臂 )作为 dp d p 的第二维, dp[i][j] d p [ i ] [ j ] 表示挂上前 i i 个砝码得到平衡度的方案数。这样可以得到平衡度任意取值时的方案数的状态表示,进而可以计算方案总数。平衡度最大值为15 * 20 * 25 = 7500,取值范围-7500~7500,可转化为0~15000,7500为平衡态。
转移方程为 dp[i][j+c[k]∗g[i]]=∑dp[i−1][j] d p [ i ] [ j + c [ k ] ∗ g [ i ] ] = ∑ d p [ i − 1 ] [ j ]
#include<cstdio>
using namespace std;
int dp[25][15000];
int c[25];
int g[25];
int main(){
int C,G;
scanf("%d%d",&C,&G);
for(int i = 1; i <= C; ++i) scanf("%d",&c[i]);
for(int i = 1; i <= G; ++i) scanf("%d",&g[i]);
dp[0][7500] = 1;
for(int i = 1; i <= G; ++i){
for(int j = 0; j <= 15000; ++j){
if(dp[i-1][j]){
for(int k = 1; k <= C; ++k){
dp[i][j + c[k]*g[i]] += dp[i-1][j];
}
}
}
}
printf("%d\n",dp[G][7500]);
return 0;
}