zoj1234 chopsticks 经典dp

8 篇文章 0 订阅

    经典的dp题,题目大意:给出需要的组合数量和一些筷子长度,每个组合要求 a<=b<=c ,然后求所有组合的 (a-b)^2 的最小和。给出的筷子长度已经是非降序的了,有一个需要自己判断的点是:每个组合的 a、b 都是连续的,如何证明?(我也不知道TAT 网上也没找到证明过程)。 然后是观察这道题有没有dp性质,至于具体怎么判断我现在还没有形成条件反射,看了网上的题解以后,才找出状态 和 状态转移方程。

    状态: dp[i][j], 表示第 i 根筷子 到 第 n 根筷子 有 j 个组合时的最小状态。

    状态转移方程: dp[i][j]=min( dp[i+1][j] , dp[i+2][j-1] + (a[i]-a[i+1])^2 );   选择这两个状态中的较小值,就是看要不要把第 i 根筷子 与 第 i+1 根筷子作为a+b,计算结果当然是当前最小状态。(这里我是自己手动模拟计算了以后才搞明白。。深深怀疑自己的智商)。

   这道题用 int 可以搞定,数组不要开太大了。

    以下是代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>

using namespace std;
int a[5002];
int dp[5002][1008];
int doub(int x)
{
    return x*x;
}
int main()
{

    int T;
    scanf("%d",&T);
    while(T--){
        int k,n;
        memset(a,0,sizeof(a));
        memset(dp,0,sizeof(dp));
        scanf("%d%d",&k,&n);
        for(int i=1;i<=n;i++)scanf("%d",&a[i]);
        k+=8;
        dp[n-2][1]=doub(a[n-2]-a[n-1]);
        for(int i=n-3;i>=1;i--)
            for(int j=1;j<=k;j++){
                if(n-i+1<3*j) break; //如果当前包含的筷子数量小于要求组合的数量 则直接加入下一根筷子。
                if(n-i+1==3*j) dp[i][j]=dp[i+2][j-1]+doub(a[i]-a[i+1]); //如果恰好等于,则把 第 i 根 与 第 i+1 根筷子作为 a、b加入组合。
                else dp[i][j]=min( dp[i+1][j] , dp[i+2][j-1]+doub(a[i]-a[i+1]) );
            }

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



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值