1. 程式人生 > >【GarsiaWachs演算法】bzoj3229: [Sdoi2008]石子合併

【GarsiaWachs演算法】bzoj3229: [Sdoi2008]石子合併

biu~

Description

Time Limit: 3 Sec Memory Limit: 128 MB
submit

在一個操場上擺放著一排N堆石子。現要將石子有次序地合併成一堆。規定每次只能選相鄰的2堆石子合併成新的一堆,並將新的一堆石子數記為該次合併的得分。
  試設計一個演算法,計算出將N堆石子合併成一堆的最小得分。

Input

  第一行是一個數N。
  以下N行每行一個數A,表示石子數目。

Output

  共一個數,即N堆石子合併成一堆的最小得分。

Sample Input

4
1
1
1
1

Sample Output

8

HINT

對於 100% 的資料,1≤N≤40000
對於 100% 的資料,1≤A≤200

思路

看到石子合併我們首先想到的是可以Dp寫轉移方程f( i , j )表示區間[ i , j ]合併的最小代價,f(i,j)=minf(i,k)+f(k+1,j)+sum(i,j)
但是時間複雜度O(n^3),空間複雜度O(n^2)
時間和空間上都過不去
在這裡我們將一個演算法:GarsiaWachs演算法
只能說一個概要吧:
設一個序列是A[1..n],每次尋找最小的一個滿足A[k-1]<=A[k+1]的k,(方便起見設A[0]和A[n+1]等於正無窮大)
那麼我們就把A[k]與A[k-1]合併,之後找最大的一個滿足A[j]>A[k]+A[k-1]的j,把合併後的值A[k]+A[k-1]插入A[j]的後面。
有定理保證,如此操作後問題的答案不會改變。
舉個例子:
186 64 35 32 103
因為35<103,所以最小的k是3,我們先把35和32刪除,得到他們的和67,並向前尋找一個第一個超過67的數,把67插入到他後面
186 64(k=3,A[3]與A[4]都被刪除了) 103
186 67(遇到了從右向左第一個比67大的數,我們把67插入到他後面) 64 103
186 67 64 103 (有定理保證這個序列的答案加上67就等於原序列的答案)
現在由5個數變為4個數了,繼續!
186 (k=2,67和64被刪除了)103
186 131(就插入在這裡) 103
186 131 103
現在k=2(別忘了,設A[0]和A[n+1]等於正無窮大)
234 186
420
最後的答案呢?就是各次合併的重量之和:420+234+131+67=852

證明嘛,基本思想是通過樹的最優性得到一個節點間深度的約束,之後
證明操作一次之後的解可以和原來的解一一對應,並保證節點移動之後他所在的
深度不會改變。詳見TAOCP。
具體實現這個演算法需要一點技巧,精髓在於不停快速尋找最小的k,即維護一個“2-遞減序列”
樸素的實現的時間複雜度是O(n*n),但可以用一個平衡樹來優化(好熟悉的優化方法),使得最終複雜度為O(nlogn),(但是本人並不會寫)

程式碼

#include <bits/stdc++.h>
using namespace std;
inline int read(){
    int ret=0,f=1;char
c=getchar(); for(;!isdigit(c);c=getchar())if(c=='-')f=-1; for(;isdigit(c);c=getchar())ret=ret*10+c-'0'; return ret*f; } int a[50001],n,m,k; long long ans=0; int main(){ n=read();m=n; for(int i=1;i<=n;++i)a[i]=read(); a[0]=a[n+1]=2147483640; for(int i=1;i<n;++i){ for(int j=1;j<=m;++j){ if(a[j-1]<=a[j+1]){ k=j;break; } } a[k-1]+=a[k]; for(int j=k;j<m;j++)a[j]=a[j+1]; k--;ans+=a[k]; while(k>0&&a[k-1]<=a[k]){ swap(a[k-1],a[k]); --k; } a[m]=2147483640;--m; } printf("%lld",ans); return 0; }

相關推薦

GarsiaWachs演算法bzoj3229: [Sdoi2008]石子合併

biu~ Description Time Limit: 3 Sec Memory Limit: 128 MB submit 在一個操場上擺放著一排N堆石子。現要將石子有次序地合併成一堆。規定每次只能選相鄰的2堆石子合併成新的一堆,

模板題動態規劃 石子合併、括號匹配、加分二叉樹——區間dp問題及其整理

題目大意:輸入一棵樹的中序遍歷,定義一棵子樹的得分為其左子樹的加分×右子樹的加分+根的分數。求最大得分及先序遍歷注意:1、初始化r[i][i]=i,便於輸出2、初始化dp[i][i-1]=dp[i+1][i]=1。因為在區間中選取一點為root時會取到端點,即左(右)子樹為空

BZOJ 3229 [Sdoi2008]石子合併 GarsiaWachs演算法

Description   在一個操場上擺放著一排N堆石子。現要將石子有次序地合併成一堆。規定每次只能選相鄰的2堆石子合併成新的一堆,並將新的一堆石子數記為該次合併的得分。   試設計一個演算法,計

基礎演算法石子合併-版本1

此題主要是用dp求出每個範圍的最小价值,再用遞迴輸出。 時間限制: 1 Sec 記憶體限制: 64 MB 題目描述 設有n堆石子排成一排,其編號為1,2,3,…,n。每堆石子有一定的數量,例如: 13 7 8 16 21 4 18 現要將n堆石子歸併為一

python演算法合併兩個有序陣列為一個有序的大陣列(時間複雜度最低)

思路按位迴圈比較兩個陣列,較小元素的放入新陣列,下標加一(注意,較大元素對應的下標不加一),直到某一個下標超過陣列長度時退出迴圈 假設兩個源陣列的長度不一樣,那麼假設其中短的陣列用完了,即全部放入到新陣列中去了,那麼長陣列中剩下的那一段就可以直接拿來放入到新陣列中去了。#co

hdu 3007爬山演算法

題意:還是和別的一樣是個模板提,給出n個點的座標,然後求出一個點到這個點的最短距離的座標,並輸出最短距離 這個資料很水,精度要求也沒有這麼高 //#include<bits/stdc++.h> #include <iostream> #include &

hdu 3932Groundhog Build Home 爬山演算法

題意:求到n個點的最大距離最小化的點 ///爬山演算法 //是個單峰函式,還有那個bzoj3680 #include<bits/stdc++.h> #define mp make_pair #define sz(x) int((x).size()) #d

排序演算法選擇排序(Selection sort)

  0. 說明   選擇排序(Selection sort)是一種簡單直觀的排序演算法。   它的工作原理如下。   首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然後,再從剩餘未排序元素中繼續尋找最小(大)元素,然後放到已排序序列的末尾。以此類推,直到所有元素均排序完

HDU 3488 Tour (最大權完美匹配)KM演算法

<題目連結> 題目大意:給出n個點m條單向邊邊以及經過每條邊的費用,讓你求出走過一個哈密頓環(除起點外,每個點只能走一次)的最小費用。題目保證至少存在一個環滿足條件。 解題分析: 因為要求包含所有點一次的環,我們不難發現,這個環中的每個點的出度和入度均為1,所以我們不妨將每個點進行拆點,將所

HDU2255 奔小康賺大錢 (帶權二分圖最優匹配) 模板題KM演算法

<題目連結>               奔小康賺大錢 Problem Description 傳 說在遙遠的地方有一個非常富裕的村落,有一天,村長決定進行制度改革:重新分配房子。 這可是一件大事,關係到人民的住房問題啊。村裡共有n間房間,剛好有n家老百姓,考慮到每家都要有

最近公共祖先(LCA) 倍增演算法

題目連結:洛谷-P3379 ## **題目描述**: 如題,給定一棵有根多叉樹,請求出指定兩個點直接最近的公共祖先。 思路 這是一個裸的LCA問題,即求書上兩個節點的最近公共祖先。 我們可以用樹上倍增來做; 當然,在做之前我們假設不知道該演算法。那麼我們如何來做

節點的最近公共祖先 倍增演算法

題目連結:計蒜客 ## **題目描述**: 思路 這是一個裸的LCA問題,即求書上兩個節點的最近公共祖先。 我們可以用樹上倍增來做; 當然,在做之前我們假設不知道該演算法。那麼我們如何來做這種型別的題目呢? 顯然,我們可以用暴力來做,找到兩點的最近公共祖先,我們

貪心演算法49. Best Time to Buy and Sell Stock

49. Best Time to Buy and Sell Stock Say you have an array for which the ith element is the price of a given stock on day&n

poj 2069 Super Star 最小求覆蓋爬山演算法

題意:給定幾個點,要求覆蓋這些點的最小球半徑。(求到n個點的最大距離最小化的點 因為是單峰的所以可以用爬山演算法 主要是我的退火演算法板子精度達不到?    //#include<bits/stdc++.h> #include <ios

基礎演算法回溯法與八皇后問題

#include"iostream" #include"stdlib.h" using namespace std; int x[8],tot=0; bool B(int x[],int k) { int i; for(i=0;i<k;i++) if(x[i]==x[

排序演算法氣泡排序(Bubble Sort)

一、簡介 氣泡排序(Bubble Sort)也是一種簡單直觀的排序演算法。它重複地走訪過要排序的數列,一次比較兩個元素,如果他們的順序錯誤就把他們交換過來。走訪數列的工作是重複地進行直到沒有再需要交換,也就是說該數列已經排序完成。這個演算法的名字由來是因為越小的元素會經由交換慢慢“浮”到數列的頂端。 二、

三、演算法優先順序搜尋(PFS)

最基本而典型的圖搜尋演算法包括:廣度優先搜尋(BFS),深度優先搜尋(DFS),優先順序搜尋等(PFS),本文主要介紹圖的優先順序搜尋(priority-first search,DFS),本文使用的圖資料結構參見之前部落格https://blog.csdn.net/qq_18108083/ar

三、演算法DFS應用-拓撲排序

深度優先搜尋(DFS)演算法是最重要的圖遍歷演算法,基於DFS框架,可以匯出大量的圖演算法,圖的拓撲排序即為其中一個很典型的例子。 拓撲排序:給定一個有向圖,如何在保證“每個頂點都不會通過邊,指向其在此序列中的前驅頂點”這一前提下,將所有頂點排成一個線性序列。 例如: 在編寫教材時,

三、演算法深度優先搜尋(DFS)

圖演算法是個龐大的家族,其中大部分成員的主體框架都可以歸結於圖的遍歷。圖的遍歷需要訪問所有頂點一次且僅 一次,此外,圖遍歷同時還要訪問所有的邊一次且僅一次。經過一次遍歷,樹邊的頂點共同構成了原圖的一顆遍歷樹。 為防止對頂點的重複訪問,在遍歷的過程中,需要動態地設定各頂點的不同狀態,並且隨著遍

三、演算法廣度優先搜尋(BFS)

圖演算法是個龐大的家族,其中大部分成員的主體框架都可以歸結於圖的遍歷。圖的遍歷需要訪問所有頂點一次且僅 一次,此外,圖遍歷同時還要訪問所有的邊一次且僅一次。經過一次遍歷,樹邊的頂點共同構成了原圖的一顆遍歷樹。 為防止對頂點的重複訪問,在遍歷的過程中,需要動態地設定各頂點的不同狀態,並且隨著遍