[条件限制动态规划] 佳佳的筷子

佳佳的筷子

题目描述

佳佳与常人不同,吃饭用三只筷子,两根短的加一根比较长的。两只短的筷子的长度应该尽可能接近,但是最长的那根长度是无所谓的。如果一套筷子的长度分别是a,b,c(a<=b<=c),则用(a-b)*(a-b)的值表示这套筷子的质量,这个值越小,这套筷子的质量越高。
佳佳请朋友吃饭,并准备为每人准备一套这种特殊的筷子。佳佳有n(n<=5000)只筷子,他希望找到一种办法搭配好k套筷子,使得这些筷子的质量之和最小。

关于输入

第一行是两个整数n和k(n<=5000,3*k<=n)
第二行是n个整数表示筷子的长度

关于输出

输出一个整数,表示筷子质量和的最小值

例子输入
5 1
1 3 4 7 10
例子输出
1
提示信息

从小到大排序后从后向前递推

解题分析

本题明显地需要动态规划来寻找最佳的解决方案,我们不妨令dp[i][j],表示取i只筷子,拿j套筷子质量和的最小值。拿了两只筷子后,必须还要一只比这两只筷子都长的筷子,所以逆推相对来说较为轻松,并且相邻两数平方和最小,故我们先将筷子按从大到小排序,然后再规定初始条件,对于那些筷子数不足以凑足需要的筷子套数的情况,我们将其状态置为一个较大的数,这样就可以防止错取,对于取0套筷子的情况,因为一套筷子也没有,故质量和置为0。

这段程序使用动态规划算法来解决"佳佳的筷子"问题。

读取输入的整数n和k,表示筷子的数量和需要搭配的套数。

接下来,程序读取n个整数,表示每根筷子的长度,并将它们存储在数组chopsticks中。

然后,程序对筷子的长度进行降序排序,以便后续计算。

接着,程序初始化一个二维数组dp,用于存储中间计算结果。dp[i][j]表示使用前i根筷子搭配j套筷子时的质量之和的最小值。

接下来,程序设置初始条件。对于一些不可能存在的情况,如2只筷子搭配1套筷子dp[2][1],初始化为一个较大的值1e6(表示无穷大),以确保它不会被选中。

然后,程序计算dp[3][1],即使用前3根筷子搭配1套筷子时的质量之和的最小值。这里使用了题目中给出的质量计算公式:(a-b)*(a-b)。

接下来,程序使用动态规划的思想,从小到大依次计算dp[i][j]的值。对于每个dp[i][j],有两种选择:
1. 不选第i根筷子,那么dp[i][j] = dp[i-1][j],表示使用前i-1根筷子搭配j套筷子时的质量之和的最小值。
2. 选第i根筷子,那么dp[i][j] = dp[i-2][j-1] + (chopsticks[i]-chopsticks[i-1])*(chopsticks[i]-chopsticks[i-1]),表示使用前i-2根筷子搭配j-1套筷子时的质量之和的最小值加上当前筷子的质量。

最后,程序输出dp[n][k],即使用所有筷子搭配k套筷子时的质量之和的最小值。

总体思路是通过动态规划,从前往后计算每个状态的最优解,最终得到整体的最优解。通过存储中间计算结果,避免了重复计算,提高了效率。

代码实现
#include <iostream>
#include <algorithm>
using namespace std;

int chopsticks[5005];
int dp[5005][5005/3+1];

int main(){
    int n,k; cin>>n>>k;
    for(int i=1;i<=n;i++){
        cin>>chopsticks[i];
    }
    sort(chopsticks+1,chopsticks+n+1,greater<int>());
    for(int i=1;i<=k;i++){
        dp[i*3-2][i]=1e6;
        dp[i*3-1][i]=1e6;
    }
    dp[3][1]=(chopsticks[3]-chopsticks[2])*(chopsticks[3]-chopsticks[2]);
    for(int j=1;j<=k;j++)
        for(int i=3*j;i<=n;i++){
            dp[i][j]=min(dp[i-1][j],dp[i-2][j-1]+(chopsticks[i]-chopsticks[i-1])*(chopsticks[i]-chopsticks[i-1]));
        }
    cout<<dp[n][k]<<endl;
    return 0;
}

当然,使用记忆搜索法也是一个不错的选择,代码相对来说更好理解。

首先,定义常量MAXN为5000,并声明变量n、k用于存储输入的筷子数量和需要搭配的套数。

接下来,定义数组chopsticks用于存储筷子的长度,数组dp用于存储中间计算结果。这里使用了long long int类型来存储筷子长度和计算结果,以确保能够处理较大的数值。

然后定义一个辅助函数S,用于计算质量的平方,即S(x) = x*x。

接下来,程序定义了递归函数f,用于计算dp[i][j]的值。函数的参数i表示当前考虑的筷子编号,j表示剩余的套数。函数的返回值是dp[i][j]的值。

在函数f中,首先进行了一些边界条件的判断。如果i等于0,表示没有筷子可供选择,返回一个较大的值1e9。如果dp[i][j]已经计算过,直接返回其值。如果i小于3*j,表示剩余的筷子数量不足以搭配j套筷子,返回一个较大的值1e9。如果j等于0,表示已经搭配完成,返回0。

然后,在函数f中,使用递归的方式计算dp[i][j]的值。有两种选择:

  1. 不选第i根筷子,那么dp[i][j] = f(i-1,j),表示使用前i-1根筷子搭配j套筷子时的质量之和的最小值。
  2. 选第i根筷子,那么dp[i][j] = f(i-2,j-1) + S(chopsticks[i]-chopsticks[i-1]),表示使用前i-2根筷子搭配j-1套筷子时的质量之和的最小值加上当前筷子的质量。

最后,在主函数main中,程序读取输入的n和k,以及每根筷子的长度,并进行降序排序。然后,初始化dp数组为-1,表示尚未计算过。

接下来,调用函数f(n,k)计算筷子质量之和的最小值,并将结果存储在result变量中。

最后,程序输出result,即筷子质量之和的最小值。

#include <iostream>
#include <algorithm>
#define MAXN 5005
using namespace std;
int n,k;
int chopsticks[MAXN]={0},dp[MAXN][MAXN/3+1]={0};

int S(int x){
	return x*x;
}

int f(int i,int j){
	if(dp[i][j]!=-1) return dp[i][j];
	if(i<3*j) return dp[i][j]=1e9;
	if(j==0) return dp[i][j]=0;
	return dp[i][j]=min(f(i-1,j),f(i-2,j-1)+S(chopsticks[i]-chopsticks[i-1]));
}

int main(){
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++){
		scanf("%d",&chopsticks[i]);
	}
	sort(chopsticks+1,chopsticks+n+1,greater<int>());
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n/3+1;j++){
			dp[i][j]=-1;
		}
	int result=f(n,k);
	printf("%d\n",result);
	return 0;
}

  • 14
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值