算法设计——合并果子的两种不同合并方式

目录

  1. 问题描述
  2. 不同应用背景分析
  3. 代码及注释
  4. 总结
问题描述
  1. 目前有n堆果子,现在要将果子两两合并为一堆,每一次合并,可以把两堆果子合并到一起
    消耗的体力等于两堆果子的重量之和。可以看出,所有的果子经过 n−1 次合并之后, 就只剩下一堆了。合并果子时总共消耗的体力等于每次合并所耗体力之和。假定每个果子重量都为 111 ,并且已知果子的堆 数和每堆果子的数目,你的任务是设计出合并的次序方案,使多多耗费的体力最少,并输出这个最小的体力耗费值。
    例如:有3堆果子,每堆数量分别是1,2,9.可以先将1,2合并,耗费体力为3,将新堆与原来的第三堆合并,得到新堆12,耗费体力12。所以耗费的总体力为15.
不同应用背景:
  1. 合并果子时,对合并操作更严格,要求每次只能合并相邻两堆果子。
  2. 合并果子时,对合并操作无要求,可以任意合并两堆果子。
    这两种不同的问法,所对应的解题算法和思路是完全不一样的,读者遇到题目时非常容易想当然,忽略细节。
    第一种情况:涉及到区间Dp,当只有两堆时,毫无疑问直接合并;当有三堆果子时,此时的最优解依赖于两种子问题(1.先合并第一、二堆,再合并第三堆。2先合并第二、三堆。再然后合并第一堆),当涉及更多堆果子时,依次类推。这样满足最优子结构性质,大问题最优依赖于很多小问题最优,考虑Dp.用dp[i] [j]数组记录第i到第j堆的耗费的最少体力
    DP公式:
    i!=j,dp[i][j]=min(dp[i][k]+dp[k+1][j]+sum[i][j]
    i==j dp[i][j]=0;
    第二种情况:由于任意合并两堆果子,贪心算法非常明显了,即每次合并数目最少的两堆果子,重复操作n-1次。画出结构图,可以看出就是大顶堆的数据结构,考虑到每次操作都要找出最少和次少的果堆,我们就采用优先队列的存储结构,保存数据。
代码及注释

任意相邻:

#include<stdio.h>
#include<bits/stdc++.h>
using  namespace std;
int main()
{
 priority_queue<int,vector<int>,greater<int> >q;//注意加空格,(“>>"和"> >"),这里优先级从小到大,即pop出值最小的

 int n,x,i;
 int min1,min2,sum=0;
 scanf("%d",&n);
 for(i=0;i<n;i++){
  scanf("%d",&x);
  q.push(x);
 } 
 while(q.size()>1){//这里注意不能写q.empty()!=true,因为大顶堆最后一定有一个根结点存在队列中,不能被pop出去
  min1=q.top();
  q.pop();
  min2=q.top();
  q.pop();
  sum+=(min1+min2);
  q.push(min1+min2);//新堆入队
 }
 printf("%d",sum);
 return 0;
 } 

相邻合并:

#include<stdio.h>
#include<bits/stdc++.h>
#define N 201
#define inf 0x3f3f3f3f
using namespace std;
int main()
{
 //i!=j,dp[i][j]=min(dp[i][k]+dp[k+1][j]+sum[i][j]
 //i==j dp[i][j]=0;
 //求dp[1][n] 
 int n,i,j,k;
 int len;
 int a[N];
 scanf("%d",&n);
 int dp[N][N];//记录从i堆到j堆的最小体力花费 
 int sum[N][N];//记录从i堆到j堆的石子数 
 memset(sum,0,sizeof(sum));//初始化
 fill(dp[0],dp[0]+N*N,inf);//初始化
 for(i=1;i<=n;i++) scanf("%d",&a[i]);

 for(i=1;i<=n;i++){
  dp[i][i]=0;
  sum[i][i]=a[i];
 }//只有一堆时,无需合并
 for(len=1;len<n;len++){
  for(i=1;i<=n&&i+len<=n;i++){
    j=i+len;
    for(k=i;k<=j;k++){
    sum[i][j]=sum[i][k]+sum[k+1][j];
    dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+sum[i][j]);

    }
  }
 }
 printf("%d\n",dp[1][n]);
 return 0;
 } 
 int min(int x,int y)
 {
  if(x<y) return(x);
  else return(y); 
 }

总结:

学习编程,提高算法能力,应注重归纳总结,学习完一门算法之后,马上找相应的题目上机练习,逐渐练就一题多解,举一反三。
希望本例程对读者有用并能激发你的思考,最后祝刷到这条博客的朋友们,今年暴富,愿望成真!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值