poj1738 An old Stone Game 石子合并(归并) GarsiaWachs算法

题目链接:poj1738

本文转自:http://blog.csdn.net/u011328276/article/details/9669531

题意: 石子合并问题, 将相邻两堆石子合并, 每次得分是合并成新的一堆石子个数, 最后累加最小值.

解题思路:

      1. 这类题目一开始想到是DP, 设dp[i][j]表示第i堆石子到第j堆石子合并最小得分.

         状态方程: dp[i][j] = min(dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1]);

         sum[i]表示第1到第i堆石子总和. 递归记忆化搜索即可.

这类题目可以参考:hdu3506 我的博客

      2. 不过此题有些不一样, 1<=n<=50000范围特大, dp[50000][50000]开不到这么大数组.

         问题分析:

         (1). 假设我们只对3堆石子a,b,c进行比较, 先合并哪2堆, 使得得分最小.

              score1 = (a+b) + ( (a+b)+c )

              score2 = (b+c) + ( (b+c)+a )

              再次加上score1 <= score2, 化简得: a <= c, 可以得出只要a和c的关系确定,

              合并的顺序也确定.

         (2). GarsiaWachs算法, 就是基于(1)的结论实现.找出序列中满足stone[i-1] <=

              stone[i+1]最小的i, 合并temp = stone[i]+stone[i-1], 接着往前面找是否

              有满足stone[j] > temp, 把temp值插入stone[j]的后面(数组的右边). 循环

              这个过程一直到只剩下一堆石子结束.

         (3). 为什么要将temp插入stone[j]的后面, 可以理解为(1)的情况

              从stone[j+1]到stone[i-2]看成一个整体 stone[mid],现在stone[j],

              stone[mid], temp(stone[i-1]+stone[i-1]), 情况因为temp < stone[j],

              因此不管怎样都是stone[mid]和temp先合并, 所以讲temp值插入stone[j]

              的后面是不影响结果.

可以参考一下博客,里面有一些每步是如何变化的:石子合并的GarsiaWachs算法

#include<iostream>
#include<iostream>
#include<cstring>
#include<string.h>
#include<algorithm>
#include<cstdio>
using namespace std;
#define maxn 55555
int a[maxn],n,num,result;
void combine(int k)
{
	int i,j;
	int temp=a[k-1]+a[k];//a[k-1]和a[k]合并为一个  所以后面的往前一位
	result+=temp;
	for(int i =k;i<num-1;i++)
	{
		a[i]=a[i+1];
	}
	num--;
	for(j=k-1;j>0&&a[j-1]<temp;j--)//往前查找一个a[j-1]比tmp大的  然后将tmp放在a[j]的位置
	{
		a[j]=a[j-1];
	}
	a[j]=temp;
	while(j>=2&&a[j-2]<=a[j])//判断是否需要继续合并
	{
		int d=num-j;//因为这个三者中 合并前两个  所有对后面的个数没有影响
		combine(j-1);
		j=num-d;
	}
}
int main()
{
	while(~scanf("%d",&n))
	{
		if(n==0)break;
		for(int i=0;i<n;i++)
		{
			scanf("%d",&a[i]);
		}
		num=1;
		result=0;
		for(int i=1;i<n;i++)
		{
			a[num++]=a[i];
			while(num>=3&&a[num-3]<=a[num-1])//如果数组中至少存在3个数时候 开始进行合并
				combine(num-2);

		}
		while(num>1)//这里情况是  a数组中个数均是由大到小合并 所以三者都是挑后两者先合并
			combine(num-1);
		printf("%d\n",result);
	}
	return 0;
}



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值