Description
问题描述:在一个操场的四周摆放着n 堆石子。现要将石子有次序地合并成一堆。规定在合并过程中最多可以有m(k)选 k 堆石子合并成新的一堆,2≤k≤n,合并的费用为新的一堆的石子数。试设计一个算法,计算出将n 堆石子合并成一堆的最小总费用。编程任务:对于给定n堆石子,编程计算合并成一堆的最小总费用。
Input
文件的第1 行有1 个正整数n,表示有n 堆石子。第2行有n个数,分别表示每堆石子的个数。第3行有n-1 个数,分别表示m(k)(2≤k≤n)的值。
Output
最小总费用。问题无解时输出“No Solution!”
Sample Input
7
45 13 12 16 9 5 22
3 3 0 2 1 0
Sample Output
136
题解
先Dfs判断是否有解,再贪心合并。注意此处Dfs要优先判断较大的合并个数行不行,因为贪心合并时,合并的次数越少,总费用就越小。贪心合并时策略为:先用少的合并次数合并较小的石子。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
priority_queue<int,vector<int>,greater<int> >Q;//小的在前的优先队列
const int N=120;
int n,m[N],Resm[N],Ansm[N],flag;
void Read()
{
int num;
scanf("%d",&n);
for(int i=1;i<=n;++i)
{
scanf("%d",&num); Q.push(num);
}
for(int i=2;i<=n;++i) scanf("%d",&m[i]);
}
void Dfs(int Res,int Now)//Res为剩余合并堆数,Now为当前可合并的堆数
{
if(Now<=1||flag) return;//搜索完毕或找到答案
int i=Res/(Now-1);
if(i>m[Now]) i=m[Now];
for(;i>=0;--i)
{
Resm[Now]=i,
Res-=i*(Now-1);
if(Res==1)
{
flag=1;
for(int j=2;j<=n;++j) Ansm[j]=Resm[j];//记录答案
return;
}
if(Res>=Now-1) Dfs(Res,Now-1);
else Dfs(Res,Now-2);
Res+=i*(Now-1);
Resm[Now]=0;
}
}
int guffman()//贪心合并
{
int Res,Ans=0;
for(int i=2;i<=n;++i)
for(int j=1;j<=Ansm[i];++j)
{
Res=0;
for(int k=1;k<=i;++k) Res+=Q.top(),Q.pop();
Q.push(Res); Ans+=Res;
}
printf("%d
",Ans);
}
int main()
{
Read(),Dfs(n,n);
if(!flag) puts("No Solution!");
else guffman();
return 0;
}