题目链接: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
-2 3
3 4 5 8 1
3 3
-1 0 1
1 2 3
代码:
#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原创,但可以转载,因为我们是兄弟。