hdu1500 Chopsticks(线性dp经典题)

题目

T(T<=20)组样例,共K+8(0<=K<=1e3)个人选筷子,每人选3根,

共N(3K+24<=N<=5e3)根筷子,第i根筷子长度为Li(1<=Li<=32000),保证输入是按非严格增序输入

对于每个人选的三根筷子A、B、C(不妨A<=B<=C),给他带来的不满意度为(B-A)*(B-A)

现在要令K+8个人的总不满意度和最小,输出最小的和

思路来源

https://blog.csdn.net/u011483306/article/details/41677559

题解

考虑dp的无后效性,长筷子得优先被废掉,所以排序排成非严格降序,倒序读入,巧妙之极orz

dp[i][j]表示只考虑前i个人前j根筷子时的最小不满意度和,

首先,一个人取的筷子肯定是相邻的,A<=B<=C<=D,可证AB一组+CD一组最小

注意到恰好3*i根筷子时,权值最小的筷子必取,从而权值最小的两根筷子必取

则有dp[i][3*i]=dp[i-1][3*i-2]+(a[3*i]-a[3*i-1])*(a[3*i]-a[3*i-1]);

而后续的转移时,是在考虑第j根筷子取不取,

不取时,j-1根筷子满足i个人;取时,j-2根筷子满足i-1个人,最后两根满足第i个人,

且由于j>3*i,必有j-2>3*(i-1)+1,即给第i个人留了一根浪费的筷子

4根筷子两种情况会保留最优的,感觉就是把两个黑块往右划和补充新的两个黑块的过程,

这样感性理解,就能保证了左侧那根长的筷子(也就是白块)一定存在了

dp还是像数学归纳一样,强调每一步,第一步合法,转移合法,则所有都合法

代码

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int t,k,n,a[5005];
int dp[1005][5005];//dp[i][j]:只考虑前i个人前j根筷子的最小代价 
int main()
{
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d%d",&k,&n);
		for(int i=n;i>=1;--i)
		scanf("%d",&a[i]);
		memset(dp,0,sizeof dp);
		for(int i=1;i<=k+8;++i)
		{
			dp[i][3*i]=dp[i-1][3*i-2]+(a[3*i]-a[3*i-1])*(a[3*i]-a[3*i-1]);
			for(int j=3*i+1;j<=n;++j)
			dp[i][j]=min(dp[i][j-1],dp[i-1][j-2]+(a[j]-a[j-1])*(a[j]-a[j-1]));
		}
		printf("%d\n",dp[k+8][n]);
	}
	return 0;
}

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Code92007

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值