zoj1234 chopsticks 经典dp

来源:互联网 发布:交易平台软件使用协议 编辑:程序博客网 时间:2024/06/06 06:36

    经典的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