石子合并问题
有若干堆石子,每次只能合并相邻石子堆,每次合并的开销是两堆石子总和。求合并所有石子的最小开销。
首先有一个算法叫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)。当区间小到3\2\1我们就可以直接做出决策了。