// 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时,数组初始全部置为无效解,然后就只求有效解就可以了.