// 1644K 16MS G++
#include <cstdio>
#include <cstring>
#include <cstdlib>
using namespace std;
#define MAX_W 25
#define MAX_W_NUM 20
#define MAX_L 15
#define MAX MAX_W*MAX_L*MAX_W_NUM
#define MAXLENGTH MAX*2 + 1
#define INF 99999
int DP[22][MAXLENGTH];
int C;
int G;
int CArray[22];
int WArray[22];
int WSum[22]; // acculate of the 1~i weight
// int Wsum;
int leftLongestLength;
int rightLongestLength;
int leftMAX;
int rightMAX;
int solve() {
memset(DP, 0, sizeof(DP)); // init 0 means no choose plan for such i weight and such weight differences
leftMAX = WSum[G] * leftLongestLength;
rightMAX = WSum[G] * rightLongestLength;
// printf("%d %d %d %d %d\n", leftLongestLength, rightLongestLength, leftMAX, rightMAX, WSum[G]);
for (int i = 0; i <= G; i++) { // has processed i weight
// j means the weight differnece(WD) between left and right
// int curleftMAX = WSum[i] * leftLongestLength; // if has choosed i weight, the leftMax:
// int curRightMAX = WSum[i] * rightLongestLength; // if has choosed i weight, the rightMax:
if (i == 0) { // if no weight is choosed.
DP[0][leftMAX] = 1; // only WD == 0 is a choose plan.
continue;
} else if (i == 1) { // if only choose 1st weight
for (int k = 0; k < C; k++) { // try all hook
DP[1][WArray[0]*CArray[k] + leftMAX] = 1; // a valid plan
// printf("%d\n", WArray[0]*CArray[k] + leftMAX);
}
continue;
}
for (int j = 0; j <= leftMAX + rightMAX; j++) { // in real, 0 means -leftMax, rightMAX + leftMAX means rightMAX
if (i >= 2) {
for (int k = 0; k < C; k++) { // try all hook
int addWeight = WArray[i-1]*CArray[k]; // hang weight i on hook k
// printf("%d %d %d %d", i, j, DP[i-1][j - addWeight + leftMAX], );
int newWeight = j - addWeight;
if (newWeight >=0 && newWeight <= leftMAX + rightMAX) {
DP[i][j] += DP[i-1][j - addWeight];
}
}
}
}
}
printf("%d\n", DP[G][leftMAX]);
}
int main() {
while(scanf("%d %d", &C, &G) != EOF) {
// maxW = -99999;
leftLongestLength = 0;
rightLongestLength = 0;
for (int i = 0; i < C; i++) {
scanf("%d", CArray + i);
if (i == 0) {
if (CArray[0] < 0) {
leftLongestLength = -CArray[0];
}
}
if (i == C-1) {
if (CArray[C-1] > 0) {
rightLongestLength = CArray[C-1];
}
}
}
WSum[0] = 0;
for (int i = 0; i < G; i++) {
scanf("%d", WArray + i);
WSum[i+1] = WSum[i] + WArray[i];
// printf("%d\n", WSum[i+1]);
}
solve();
}
}
变种DP题,刚看题时,有强烈的穷举冲动,不过后来算了一下,穷举估计是做死.
就老老实实DP了,这道题应该算一个中阶(对我而言)变种DP,因为转化并不是很容易能看出来,自己想了好久+搜了一下才确定了思路,
这道题的DP[i][j]的含义应该是:已经挂了前i个weight,并且在平衡因数B(右边的weight权值 - 左边的weight权值)是j的情况下的挂法的数量。
首先要做的是负转正,因为 B可能会是<0(这样就不能用数组表示了),因此要给B加一个数T来保证在运算中,B+T永远是>=0的,而T呢,根据题目给范围,
最多20个weight,每个weight最多重25, 而平衡壁最长为15,因此在20个重量为25的weight全部挂在左边的平衡壁15的位置下,B可以取的最小值:-20*25*15,
那么T就好办了,就取20*25*15就可以了,这一步解决以后,就可以递归求DP[i][j](初始全部set为0,表示没有挂法)了,
DP[i][j]的3个case:
1. i==0, 一个weight都没有挂,那么只有B==0才是唯一合法的挂法(什么都不挂,必然B==0), DP[0][0+T] = 1。
2. i== 1, 只挂第一个weight,那么B的取值就是 W[1] * C[i](C[i] 代表每个挂钩的位置和权值),只有 D[1][W[1] * C[i] + T] == 1, 表示在只挂第一个的情况下,这些权值是可以挂出来的.
3. i>1, 这种情况,要挂第ith个weight, 那么根据D[i][j]的含义,如果第ithweight挂在kth的位置,那么B 增加的值就是 W[i]*C[k],
如果要求在增加了ith weight以后,B仍旧是 j, 那么就要求 找到一个 D[i-1][j - W[i]*C[k]]的挂法,如果该挂法存在,那么D[i][j] 就+1, 注意的是,j - W[i]*C[k]可能会突破 0~2T的范围,那么这种情况下,这种挂法是不存在的,直接检查下一个 ith weight的悬挂位置即可,直到为ith weight检查完了所有的悬挂位置.
因此 D[i][j] = SUM( D[i-1][j - Cj] )(Xi 是ith weight 挂在 悬挂位置j 增加的B值).
本题似乎没有办法使用滚动数组来实现DP数组的维度压缩,因为D[i][j]的值 可能依赖 D[i-1][j1] (j1 > j) 和 D[i-1][j2] (j2 < j), 是一种双向依赖,而滚动数组要求必须是单向依赖。
一开始还想多了,还想求出每个i情况下B的上下限,然后将DP数组超出此上下限的置为0, 后来发现,初始就已经全部为0了,因此DP时,数组初始全部置为无效解,然后就只求有效解就可以了.