石子合并问题

有若干堆石子,每次只能合并相邻石子堆,每次合并的开销是两堆石子总和。求合并所有石子的最小开销。

首先有一个算法叫GarsiaWachs。这个算法思想是,当有三堆石子 a,b,c,其合并开销有两种:先合并ab,(a+b)+((a+b)+c),先合并bc,(b+c)+((b+c)+a)=>a,c即判断a和c的大小关系。当a<c,就应该先合并ab。故我们从左向右遍历是否有a<c,有的话就合并ab。然后把它插到左边比它大的第一个数右边(这个操作不会影响答案,大家可以自己证明以下)。反复上述操作,最后结果就是最佳方案后的一堆石子了。为了编码方便,我们一般把数组的0和n+1置为inf(石子堆编号从1~n)。

据说可以用平衡树优化,等我去看平衡树再来更新一波。

 
#include <iostream>
#include <string>
#include <vector>
#include <cmath>
#include <algorithm>
#include <stack>
using namespace std;
const int inf=9999999;
int main(int argc, char *argv[])
{
    cin.sync_with_stdio(false);
    int n;
    int num[205];
    int dp[205];
    while(cin>>n)
    {
        num[0]=num[n+1]=inf;
        for(int i=1;i<=n;i++)
            cin>>num[i];
        int ans=0;
        while(n!=1)
        {
            //cout<<n<<endl;
            for(int i=1;i<=n;i++)
            {
                if(num[i-1]<=num[i+1])
                {
                    //cout<<num[i-1]<<' '<<num[i+1]<<endl;
                    num[i-1]+=num[i];
                    ans+=num[i-1];
                    for(int j=i+1;j<=n;j++)
                        num[j-1]=num[j];
                    num[n]=inf;
                    n--;
                    int temp=num[i-1];
                    for(int j=i-1;j>0;j--)
                    {
                        if(num[j-1]<temp)
                            num[j]=num[j-1];
                        else
                        {
                            num[j]=temp;
                            break;
                        }
                    }
                   
                    break;
                }
            }
        }
        cout<<ans<<endl;
    }
    return 0;
}
        

还有种算法是基于动态规划的,如果我们要合并i~j堆石子,其开销设为dp[i][j],dp[i][j]=min(dp[i+1][k]+dp[k+1][j]+sum(i,j)) (i<=k<=j)。当区间小到321我们就可以直接做出决策了。

原文地址:https://www.cnblogs.com/LukeStepByStep/p/6696553.html