POJ 1837 Balance

题目链接
题意:一个杠杆,有多个钩子,每个钩子可以挂多个砝码,现在给出钩子距离中心点的位置坐标以及各个砝码的重量,问在将所有砝码都用上的前提下,使这一杠杆平衡的方法有多少种?
思路:多重背包,dp[i][j]数组中存储的是到第 i 个砝码位置平衡度为 j 的方法有几种
#include<stdio.h>
#include <stdlib.h>
#include <iostream>
using namespace std;
砝码个数G  (2 <= G <= 20)
每个砝码的重量1 <= weight <= 25
杠杆上的钩子的位置离中心的位置介于 0 到 15 之间,且个数 C (2 <= C <= 20)
int dp[21][15001];    平衡度向后顺延7500,即平衡度为0 变成7500
int hooks[21];        钩子的位置数组,递增顺序
int weights[21];      砝码的重量数组,递增顺序
int offset = 7500;    位移
int main()
{
	int  i, j, k, c, g, max1, max2, val;
	scanf("%d%d", &c, &g);
    for( i = 1; i <= c; i ++ )
		scanf("%d", &hooks[i]);
	int sum = 0;
	for( i = 1; i <= g; i ++ ){
		scanf("%d", &weights[i]);
		sum += weights[i];
	}
	
	max1 = sum * hooks[1] + offset;        //最小平衡度,即所有的砝码都挂在最左的钩子上
	max2 = sum * hooks[c] + offset;		   //最大平衡度,即所有的砝码都挂在最右的钩子上

	dp[0][0 + offset] = 1;

	for( i = 1; i <= g; i ++ ){
		for( j = 1; j <= c; j ++ ){			
			for( k = max1; k <= max2; k ++ ){
				val = k + weights[i] * hooks[j];
				dp[i][val]  = dp[i - 1][k] + dp[i][val];   ///动态转移方程
			}
		}
	}
	printf("%d\n", dp[g][0 + offset] );
	return 0;
}
(以下转自 discuss)
其实感觉 像此题这种类型的并不属于dp范畴

虽然程序看起来使用的是递推这一过程,但总不能说开个二重循环就是dp吧

如果只从求解上来讲(不考虑数据值的范围), 只有枚举这唯一途径, 而此题的复杂度为O(20^20), 大约等于 10^26, 微机是很难在短时间内求解的。

而此题还有另一个限制, 就是数据值的范围, 虽然状态数那么多, 但是它们的范围有限

这个时候, 状态产生大量重复, 于是, 程序可以优化为压缩这些重复状态, 从而减少开销

在实现的过程中, 确实划分了阶段, 但这个过程更像是枚举, 优化过后的枚举

在朴素枚举当中, 阶段数与复杂度呈指数关系, 而在此题这种特殊情况下, 每个阶段数的最大不同状态数是有限的, 压缩以后就只是常数关系了

所以那 'dp'方程更像是一个'压缩状态'过程, 并没有求最优这一意思, 最后输出是状态数, 并不是最优解

再看一些常见的dp问题, 它们的意义很明确, 一定是求某种最优的结果, 其中普遍存在max,min

最为关键的一点, 凡是能够被dp的问题, 都是p类问题, 它们的复杂度都为多项式表达式

而该题类似的问题, 它们似乎往往都是npc问题, 它们很难有特别有效的算法

像本题这种就是通过狭小的数据范围来进行优化, 但仍然逃不脱枚举这一本质, 

如果稍微加大数据范围, 您看看还有更好的方法么?

                
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值