题目链接:http://poj.org/problem?id=1837
题目大意:天平平衡问题,给定m个可以放置砝码的位置,左边为负数,右边为正数,然后给定n个砝码,重量从1..25不等,位置和重量都不会出现相同的值。最后问让天平平衡的放置方法数。
解题思路:将砝码的重量乘以各个位置得到一个n*m的矩阵,表示第i个砝码在第j个位置的权值,算出权值后每一行算一组,因为只可能防放置在一个地方,那这样问题就转换为求最后的权值和为0的方案数的分组背包。转换之后,还要考虑一个问题,权值为负数的时候怎么处理,自己写个hash函数,把负数映射到正权值之和没办法到达的地方。在写的时候为了增加效率,把正权值和和负权值和给算了出来,然后作为容量的上下界,没想到这个竟然有错,无奈把上下界改成8000和-8000就过了,因为正权值最大为20 * 25 * 15 = 7500,各大一点保险。
状态转移方程: dp[i][Gethash(j)] += dp[i-1][Gethash(j-val[i][k])]; (1 <= i <= n, 1 <= k <= m,GetHash函数为映射函数),复杂度O(16000*N*M).
测试数据:
2 5
代码:
#include <stdio.h> #include <string.h> #define MIN 50 #define MAX 10000 int n,m,dp[MIN][MAX*2]; int val[MIN][MIN],pos[MIN]; int GetHash(int x) { if (x < 0) return MAX - x; else return x; } int Solve_1A() { int i,j,k; for (k = 1; k <= m; ++k) dp[1][GetHash(val[1][k])] = 1; for (i = 2; i <= n; ++i) for (j = 8000; j >= -8000; --j) for (k = 1; k <= m; ++k) dp[i][GetHash(j)] += dp[i-1][GetHash(j-val[i][k])]; return dp[n][0]; } int main() { int i,j,k; while (scanf("%d%d",&m,&n) != EOF) { //n 表示有几种weight,m表示几个位置 memset(dp,0,sizeof(dp)); for (i = 1; i <= m; ++i) scanf("%d",&pos[i]); for (i = 1; i <= n; ++i) { scanf("%d",&k); for (j = 1; j <= m; ++j) val[i][j] = k * pos[j]; } int ans = Solve_1A(); printf("%d\n",ans); } }
本文ZeroClock原创,但可以转载,因为我们是兄弟。