您的位置:首页 > 运维架构

zoj1234 chopsticks 经典dp

2016-02-12 17:41 162 查看
经典的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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: