多元Huffman编码变形

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\n",Ans); 
}

int main()
{
	Read(),Dfs(n,n);
	if(!flag) puts("No Solution!");
	else guffman();
	return 0;
}
posted @ 2019-08-20 09:01  OItby  阅读(165)  评论(0编辑  收藏  举报