poj 1837 balance DP 模拟赛 2017.10.10

题目描述
有一个天平,天平左右两边各有若干个钩子,总共有 C 个钩子,有 G 个钩码,求将钩码全部挂到钩子上使天平平衡的方法的总数(∴数据保证钩码全挂上时才有可能达到平衡)。
输入描述
第一行两个数 c, g 分别代表钩子数和钩码数
第二行 c 个数,表示每个钩子距离天平中央的距离 c[i],
负数表示在左边,正数表示在右边
第三行 g 个数,表示每个钩码的重量 w[i]
输出描述
输出总方案数
样例输入
2 4
-2 3
3 4 5 8
样例输出
2
数据范围及提示
30%: c<=9, g<=9
100%: c<=20, g<=20, -15<=c[i]<=15, w[i]<=25

分析:
//初中物理:力矩的量纲是力×距离,∴保证两边力*距离的和相等即可
//对于左边或右边,每个钩码有放或不放两种状态,每种方法会产生不同的影响
//求方案数,∴考虑dp 

dp[i][j]:选取i个钩码,平衡度为j时的方案数
//两个决定因素:必须平衡,平衡则必须用完所有的钩子,所以设计状态时考虑这两个,而且这两个因素受上一个选取了的钩子的影响,因为上一个选上了当前选取了的钩码数会+1,平衡度会变化,可以进行状态转移(递推) 

最终所求状态:平衡,也就是左+右=0,所以定义一个平衡度为0 
if <0偏左 , >0 偏右
又因为数组下标不能为负数,最坏的情况就是都挂在左边或右边,
此时 力矩=25*15*20=7500,也就是j∈[-7500,7500],
又因为数组下标不能为负数,∴扩大一倍j∈[0,15000],
平衡时,天平平衡度变为了7500

\(^o^)/小优化:因为if语句,枚举第一遍时,dp都=0,但以后,就不一定了,
也就是说,之前搜到的j状态下的dp一定=0,也就是第一遍搜的时候,
之前没搜过,所以j状态一定没有出现过,所以一定=0 
∵i-1个不可能平衡 (数据保证钩码全挂上时才有可能达到平衡)
∴没有必要搜,跳过   
当i!=0的时候再继续往下搜,这样更快
#include<iostream>
#include<cstdio> 
#include<cstring>
using namespace std;

int g,m,l[21],w[21],dp[21][15000];

int main()
{
    //freopen("balance.in","r",stdin);
    //freopen("balance.out","w",stdout);

    scanf("%d%d",&g,&m);
    for(int i=1;i<=g;i++) scanf("%d",&l[i]);
    for(int i=1;i<=m;i++) scanf("%d",&w[i]);

    memset(dp,0,sizeof(dp));
    dp[0][7500]=1;//初始时状态只有一个,一个钩码也不挂,平衡

    for(int i=1;i<=m;i++)
        for(int j=0;j<=15000;j++)
            if(dp[i-1][j])//=0 则跳过,小优化(见“分析”)
            //!=0继续向下搜
                for(int k=1;k<=g;k++)//枚举钩子的距离
                    dp[i][j+w[i]*l[k]]+=dp[i-1][j]; 

    printf("%d\n",dp[m][7500]);
    return 0;
}




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值