1. 程式人生 > >NOIP複賽複習(十五)動態規劃鞏固與提高

NOIP複賽複習(十五)動態規劃鞏固與提高

經典例題:數字金字塔(Luogu 1216 

寫一個程式來查詢從最高點到底部任意處結束的路徑,使路徑經過數字的和最大。每一步可以走到左下方的點也可以到達右下方的點。

我們現在這裡討論搜尋如何實現: 

狀態:目前在第x行第y 

行動:向左走,向右走 

例如:一個底邊為4的三角形共有八種狀態:

我們按照一般的搜尋思路,進行深度優先搜尋:

 

void dfs(int x,int y,int val)

{

    val+=a[x][y];//

加上權值

    if(x==n-1)

    {

        if(val>ans)ans=val;//更新更大的ans

        return;

    }

    dfs(x+1,y,val);//往左邊走

    dfs(x+1,y+1,val);//往右邊走

}


考慮時空效率,DFS確實很暴力啊,有沒有什麼優化呢?? 

我們引入冗餘搜尋這個概念:無用的,不會改變答案的搜尋 

例子:觀察下面兩個例子。用兩種方式都能到達第 3 行第 2 列,只是路徑不同,同時走到這個點兩條路權值和不一樣,其中一個總和為 8,一個總和 12 

那麼可以觀察可得,總和為 8 的搜尋是冗餘的(不會改變答案),即使不繼續搜尋,答案也不會改變。 

因為 12 往下搜尋,無論往左往右,都會比

 8 對應的路徑大。

可見,冗餘就是剪枝的,那麼如何利用冗餘搜尋,來優化程式呢? 

我們可以對於每一個位置記錄一個值 F,代表搜尋到此位置時,最大的路徑和是多

少,這樣如果搜到某一個位置時候,路徑和不大於記錄值F,說明這個搜尋是冗餘搜尋,直接退出,如果大於,就需要更新F值並且繼續搜尋。 

我們就把這種搜尋叫做記憶化搜尋,根據之前的記憶來優化搜尋;在這道題中,每個位置的記憶就是最大的路徑和

 

//T1:數字金字塔(記憶化搜尋)

void dfs(int x,int y,int val)

{

    val+=a[x][y];

    // 記憶化過程

    if(val<=f[x][y])return;//發現冗餘搜尋,退出

    f[x][y]=val;//f[x][y]記錄這個點當前最大權值

    if(x==n-1)//如果搜到了最後一個點,ans更新儲存最大值,退出即可

    {

        if(val>ans)ans=val;

        return;

    }

    dfs(x+1,y,val);//繼續搜尋

    dfs(x+1,y+1,val);

} 

 

101揹包問題

經典例題:採藥(Luogu 1048 

辰辰是個天資聰穎的孩子,他的夢想是成為世界上最偉大的醫師。為此,他想拜附近最有威望的醫師為師。醫師為了判斷他的資質,給他出了一個難題。醫師把他帶到一個到處都是草藥的山洞裡對他說:孩子,這個山洞裡有一些不同的草藥,採每一株都需要一些時間,每一株也有它自身的價值。我會給你一段時間,在這段時間裡,你可以採到一些草藥。如果你是一個聰明的孩子,你應該可以讓採到的草藥的總價值最大。 

草藥 1 :時間 71;價值 100

草藥 2 :時間 69;價值 1

草藥 3 :時間 1 ;價值 2

@最優選擇:草藥 2+3:總時間 70;總價值 3 

這題是經典的揹包問題,和金字塔問題不同的地方在於:它是有重量限制的。 

我們還是先用記憶化搜尋來思考這個問題: 

狀態:目前已經決定到第x件物品,當前揹包中物品總重量為w,總價值為v 

行動:這件物品取還是不取; 

約束:物品總重量不超過w(揹包總重量); 

目標:物品總價值最大; 

比較下列兩種情況: 

狀態相同:x1=x2(當前搜尋到同一件物品)w1=w2(當前總重量相等) 

價值不同:但它們的揹包總價值不同,其中v1<v2。(經過不同的路徑到達同一個點,但是後者的val更大)

則我們可以說狀態1為冗餘的,因為它肯定比狀態2要差。

*記憶化:對於每個狀態(xw),記錄對應的v的最大值。

 

//T5:採藥(記憶化搜尋)

void dfs(int t,int x,int val)//t為剩餘時間,x為當前決定的第幾株草藥,val為總價值

{

    //記憶化

    if(val<=f[t][x])return;

    f[t][x]=val;

    if(x==n)//把草藥採摘完了,直接返回

    {

        if(val>ans)ans=val;//更新最大值ans

        return;

    }

    dfs(t,x+1,val);

    if(w[x]<=t)dfs(t-w[x],x+1,val+v[x]);//如果我們還有時間,繼續採摘!

}

 

那好的,說完記憶化搜尋我們回到正題:動態規劃啦!記憶化搜尋是DP的基礎。 

我們再回到數字金字塔這個問題來,下圖的黑色三角形是我們記憶化搜尋的路徑,我們想想,是不是可以不通過記憶化搜尋就能得到這個黑色三角形??

最優性:設走到某一個位置的時候,它達到了路徑最大值,那麼在這之前,它走的每一步都是最大值。 

-考慮這條最優的路徑:每一步均達到了最大值

 

最優性的好處:要達到一個位置的最優值,它的前一步也一定是最優的。 

-考慮圖中位置,如果它要到達最優值,有兩個選擇,從左上方或者右上方的最優值得到:

 

所以從這裡,定義動態規劃(DP):只記錄狀態的最優值,並用最優值來推匯出其他的最優值。 

記錄 F[i][j] 為第 i 行第 j 列的路徑最大值,有兩種方法可以推導:(兩個分支兩種狀態,選取最大) 

@順推:用 F[i][j] 來計算 F[i+1][j],F[i+1][j+1] 

@逆推:用 F[i-1][j],F[i-1][j-1] 來計算 F[i][j] 

這兩種思考方法也是動態規劃中最基本的兩種方法,解決絕大部分DP我們都可以採用這樣的方法。

  

//T2:數字金字塔-順推(有點類似於記憶化搜尋的思路)

f[0][0]=a[0][0];

for(int i=0;i<n-1;++i)

for(int j=0;j<=i;++j)//f陣列為最優值路徑(黑色金字塔,a為源資料陣列(紫色金字塔)

{

    //分別用最優值來更新左下方和右下方

   f[i+1][j]=max(f[i+1][j],f[i][j]+a[i+1][j]);//和當前的f[i+1][j]比較

   f[i+1][j+1]=max(f[i+1][j+1],f[i][j]+a[i+1][j+1]);//和當前的f[i+1][j+1]比較

}

 

//T4:數字金字塔-逆推(自頂向下)

f[0][0]=a[0][0];

for(int i=0;i<n;++i)//單獨處理

{

   f[i][0]=f[i-1][0]+a[i][0];//最左的位置沒有左上方

   f[i][i]=f[i-1][i-1]+a[i][i];//最右的位置沒有右上方

    for(intj=1;j<i;++j)//在左上方和右上方取較大的

   f[i][j]=max(f[i-1][j-1],f[i-1][j])+a[i][j];

}

//答案可能是最後一行的任意一列

ans=0;

for(int i=0;i<n;++i)

ans=max(ans,f[n-1][i]);

 

*轉移方程:最優值之間的推導公式。 

@順推: 

F[i+1][j]= MAX (F[i][j] + a[i+1][j]); 

F[i+1][j+1]= MAX (F[i][j] + a[i+1][j+1]); 

@逆推: 

F[i][j]= MAX (F[i-1][j], F[i-1][j-1]) + a[i][j]; (注意!逆推時要注意邊界情況! )

順推和逆推本質上是一樣的(複雜度一致);順推和搜尋的順序類似;而逆推則是將順序反過來;順推考慮的是我這個狀態的下一步去哪裡” ,逆推的考慮的是從什麼狀態可以到達我這裡” 。同時在轉移的過程中我們要時刻注意邊界情況。 

我們還可以改變搜尋順序:

 

//T3:數字金字塔-逆推/路徑自底向上

//改變順序:記錄從底部向上走的路徑最優值

for(int i=0;i<n;++i)

f[n-1][i]=a[n-1][i];//備份底部自己這一行

//逆推過程:可以從左下方或右下方走過來;沒有邊界情況

相關推薦

NOIP複賽複習動態規劃鞏固提高

經典例題:數字金字塔(Luogu 1216)  寫一個程式來查詢從最高點到底部任意處結束的路徑,使路徑經過數字的和最大。每一步可以走到左下方的點也可以到達右下方的點。 我們現在這裡討論搜尋如何實現:  狀態:目前在第x行第y列 

NOIP複賽複習字串演算法鞏固提高

一、Trie樹   1.定義: 通過字串建成一棵樹,這棵樹的節點個數一定是最少的。例如:4個字串"ab","abc","bd","dda"對應的trie樹如下: 其中紅色節點表示存在一個字串是以這個點結尾的。  一個性質:在樹上,兩個點u,

NOIP複賽複習數論演算法鞏固提高

一、數論    1.數   整數、自然數(大於等於0的整數)、正整數(大於0的整數)、負整數、非負整數、非正整數、非零整數、奇數偶數。   2.整除性   設a,b∈Z,如果存在c∈Z並且a=bc,則

NOIP複賽複習基礎演算法鞏固提高

一、倍增演算法:   定義:用f[i][j]表示從i位置出發的2j個位置的資訊綜合(狀態) 一個小小的問題:為什麼是2j而不是3j,5j,…?因為,假設為kj,整個演算法的時間複雜度為(k-1)logk,當k=2時,時間複雜度最小。 這個演算法的三個應用:

NOIP複賽複習怎樣才能拿到高分?

摘要 考場策略和程式測試是資訊學競賽中非常重要的環節,很多優秀的選手在很多比賽中總是會在這兩個環節上犯下這樣和那樣的錯誤,導致得到的分數和實力不成正比,最後留下了無盡的遺憾。本文將探討一些這兩個環節上值得注意的地方,提出一些可行的方法,分享一些經驗,以此希望幫助選手們在比賽中發揮水平,減少失

C++語言學習——C++抽象類接口

eight close send 都是 () 實例 amp 定義類 esp C++語言學習(十五)——C++抽象類與接口 一、抽象類與接口 1、抽象類簡介 面向對象的抽象類用於表示現實世界的抽象概念,是一種只能定義類型,不能產生對象的類(不能實例化),只能被繼承並被重寫相關

java基礎學習總結:抽象類介面

       抽象類與介面是java語言中對抽象概念進行定義的兩種機制,正是由於他們的存在才賦予java強大的面向物件的能力。他們兩者之間對抽象概念的支援有很大的相似,甚至可以互換,但是也有區別。  一、抽象類    &n

NOIP複賽複習程式對拍圖論模板

程式對拍 所謂“對拍”,顧名思義,就是讓兩者相互比對。所謂“兩者”,一是你要測試的程式,二是一個答案在該程式在一定範圍(時間/空間)內結果必定正確的程式(一般是用暴力求解的程式)。對拍一般需要造資料程式(data.exe),保證正確性的暴力對拍程式(test.exe)與測試程式(以moo.e

NOIP複賽複習十三圖論演算法鞏固提高

一、圖的儲存   1、鄰接矩陣   假設有n個節點,建立一個n×n的矩陣,第i號節點能到達第j號節點就將[i][j]標記為1(有權值標記為權值),  樣例如下圖:   /*無向圖,無權值*/ i

NOIP複賽複習如何設計測試資料?

有些同學參加一次資訊學比賽之後,自我感覺非常不錯,但是測評結果成績卻並不理想。造成這種情況的原因有多方面,但是我認為其中不可忽視的一大原因就是在寫完程式之後,他們並不知道如何保證程式的正確性。在這裡,我就這個問題提出一點自己的看法。 先從考試的時間分配問題講起。很多同學覺得考試時間很充分,N

NOIP複賽複習STL演算法樹結構模板

STL演算法 STL 演算法是一些模板函式,提供了相當多的有用演算法和操作,從簡單如for_each(遍歷)到複雜如stable_sort(穩定排序),標頭檔案是:#include <algorithm>。常用STL 演算法庫包括:sort快速排序演算法、二分

NOIP複賽複習STL容器字串模板

STL容器 STL 容器是一些模板類,提供了多種組織資料的常用方法。常用的STL容器包括pair(組合)、list(列表,類似於連結串列)、vector(向量,類似於陣列)、priority_queue(優先佇列)、set(集合)、map(對映)、stack(棧)等,通過模板的引數

NOIP複賽複習演算法分析排序模板

演算法分析 演算法分析的目的是預測演算法所需的資源,如計算時間(CPU 消耗)、記憶體空間(RAM 消耗)、通訊時間(頻寬消耗)等,以及預測演算法的執行時間,即在給定輸入規模時,所執行的基本運算元量,或者稱為演算法複雜度。 演算法的執行時間取決於輸入的資料特徵,輸入

NOIP複賽複習讀寫外掛高精度模板

讀入輸出掛 讀入輸出掛就是逐個字元地讀入資料,從而讓讀入更加快速。輸出掛的原理也是一樣的,都是通過將輸出數字變成輸出字元以加快速度。當然輸入輸出外掛一般用在大量輸入輸出的情況下,這樣價效比才高一些,否則得不償失。 void Rd(int &res){  &nbs

NOIP複賽複習檔案讀寫數論模板

檔案讀入讀出 假設題目名為“add”,那麼資料夾名為“add”,c++程式名為“add.cpp”,讀入檔名為“add.in”,輸出檔名為“add.out”。四個的拼寫均不可有誤,包括大小寫差異。千萬不要除錯後就忘記修改檔案讀入讀出了。  #include<cstdio&

NOIP複賽複習競賽環境注意事項

一、比賽不提供紙質試題,只提供電子版試題檔案。 該檔案壓縮包儲存在計算機桌面上。監考人公佈密碼後,選手自行解密試題。 試題解壓密碼會影響一個人的心情,一定要一次輸對,注意大小寫,不要邊輸入邊檢查,要對自己有自信。 二、江蘇複賽選手上機可自選windows或linux作業系統。

NOIP複賽複習常見問題常用策略

數學類問題 1. 精度處理(高精度、實數處理、各種浮點型別處理方法) 2. 組合數學問題(斐波那契數列、第二類數、卡特蘭數、Polya原理、排列組合計數、加法原理與乘法原理) 3. 進位制問題(特定二進位制串的統計、二分查詢、利用二進位制進行路徑、狀

Spring 學習——AOP 基礎之動態代理

AOP 前奏 WHY  AOP ? 需求1-日誌:在程式執行期間追蹤正在發生的活動 需求2-驗證:希望計算器只能處理正數的運算 程式碼實現片段       問題 •程式碼混亂:越來越多的非業務需求(日誌和驗證等)

OpenCV2學習筆記:利用Cmake高速查找OpenCV函數源代碼

one 生成 img log 分享 lan 學習筆記 全部 modules 在使用OpenCV時,在對一個函數的調用不是非常了解的情況下,通常希望查到該函數的官方聲明。而假設想進一步研究OpenCV的函數,則必須深入到源碼。在VS中我們能夠選中想要查

從零開始學習htmlcss樣式設置小技巧——下

right ron pos 瀏覽器 spl pan esc 插入 ccf 六、垂直居中-父元素高度確定的單行文本 1 <!DOCTYPE HTML> 2 <html> 3 <head> 4 <meta charset="