目录
- 问题描述
- 不同应用背景分析
- 代码及注释
- 总结
问题描述
- 目前有n堆果子,现在要将果子两两合并为一堆,每一次合并,可以把两堆果子合并到一起
消耗的体力等于两堆果子的重量之和。可以看出,所有的果子经过 n−1 次合并之后, 就只剩下一堆了。合并果子时总共消耗的体力等于每次合并所耗体力之和。假定每个果子重量都为 111 ,并且已知果子的堆 数和每堆果子的数目,你的任务是设计出合并的次序方案,使多多耗费的体力最少,并输出这个最小的体力耗费值。
例如:有3堆果子,每堆数量分别是1,2,9.可以先将1,2合并,耗费体力为3,将新堆与原来的第三堆合并,得到新堆12,耗费体力12。所以耗费的总体力为15.
不同应用背景:
- 合并果子时,对合并操作更严格,要求每次只能合并相邻两堆果子。
- 合并果子时,对合并操作无要求,可以任意合并两堆果子。
这两种不同的问法,所对应的解题算法和思路是完全不一样的,读者遇到题目时非常容易想当然,忽略细节。
第一种情况:涉及到区间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);
}
总结:
学习编程,提高算法能力,应注重归纳总结,学习完一门算法之后,马上找相应的题目上机练习,逐渐练就一题多解,举一反三。
希望本例程对读者有用并能激发你的思考,最后祝刷到这条博客的朋友们,今年暴富,愿望成真!