NOIP演算法總結 關於簡單 線性動態規劃

2021-07-24 03:39:30 字數 4368 閱讀 9099

動態規劃,顯然是乙個很讓人頭疼的地方,也沒有個固定的演算法,最多就是有一些模板(比如揹包啊),要是想要增大做出來的機率,也就只好多做做題找找感覺了~

線性動態規劃可以說是dp中最簡單的型別了,當然裡面很多也是不容易的題目,也是需要一點技巧了。搞了好幾天了,現在就來總結一下。

例題1:最大子段和

題目描述

給出一段序列,選出其中連續且非空的一段使得這段和最大。

輸入輸出格式

輸入格式:

輸入檔案maxsum1.in的第一行是乙個正整數n,表示了序列的長度。

第2行包含n個絕對值不大於10000的整數a[i],描述了這段序列。

輸出格式:

輸入檔案maxsum1.out僅包括1個整數,為最大的子段和是多少。子段的最小長度為1。

輸入輸出樣例

輸入樣例#1:

7 2 -4 3 -1 2 -4 3

輸出樣例#1:【資料規模與約定】

對於40%的資料,有n ≤ 2000。

對於100%的資料,有n ≤ 200000。

【洛谷1115】

演算法一:暴力列舉。列舉每乙個子段,求和,再求最大。

時間複雜度:列舉是o(n),每次計算是o(n),總共是o(n^3)。

#include 

using

namespace

std ;

int a[200010] ;

int main()

printf ( "%d\n", ans ) ;

return

0 ;}

顯然不可取。

看那範圍,顯然是tle到乾乾淨淨。

演算法二:字首和優化。和演算法一類似,也是列舉每個子段,只是子段求和的時候使用字首和優化(o(1))。

時間複雜度:o(n^2)

#include 

using

namespace

std ;

int a[200010] ;

int main()

int ans = -2147483647 ;

for ( i = 1 ; i <= n ; i ++ )

for ( j = i ; j <= n ; j ++ )

printf ( "%d\n", ans ) ;

return

0 ;}

啊!騙到了40分,還是可以的。

不過,顯然不是正解。

演算法三:動態維護最小字首和。

時間複雜度: o(n)

這還是要分析一下的……

對於i~j的子段,sum = a[j] - a[i-1] ;

那麼,以j為結尾的所有子段,都是a[j]減去乙個什麼什麼東西。

顯然,減去的那個東西越小越好~

那麼,我們只要維護最小的那個就好了。所以時間降到了線性。

#include 

using

namespace

std ;

int a[200010] ;

int main()

int ans = -2147483647, min = 0 ;

for ( i = 1 ; i <= n ; i ++ )

printf ( "%d\n", ans ) ;

return

0 ;}

沒話說,這還不ac?

就像上面那個題目裡所使用的一樣,字首和經常能給我們很大的幫助。

那是一維的『字首和』,其實,對於一些二維甚至三維的統計(不一定是求和,也可能是求 異或 之類),我們也可使用字首和。

最大正方形【洛谷1387】

題目描述

在乙個n*m的只包含0和1的矩陣裡找出乙個不包含0的最大正方形,輸出邊長。

輸入輸出格式

輸入格式:

輸入檔案第一行為兩個整數n,m(1<=n,m<=100),接下來n行,每行m個數字,用空格隔開,0或1.

輸出格式:

乙個整數,最大正方形的邊長

輸入輸出樣例

輸入樣例#1:

4 4

0 1 1 1

1 1 1 0

0 1 1 0

1 1 0 1

輸出樣例#1:

分析:如果乙個矩形的元素和與面積相等,那麼它裡面的元素就全部是1

演算法一:列舉

列舉正方形的某乙個定點座標(o(n

× m)),在列舉正方形邊長(o(max(n,m))),再累加(o(n

× m))

時間複雜度:o(n^5)

顯然會tle的乾乾淨淨。

由於這個暴力列舉很簡單,就不貼出**了。

演算法二:還是列舉,只是加字首和優化

方法同上,但是求和的過程不是累加,而是使用字首和(o(1))。

怎麼使用字首和呢?

sum(i,j) 表示點(1,1)和點(i,j)圍成矩形裡元素的和。(如下圖)

那麼,為什麼要這樣表示呢?那是因為簡便計算矩陣和啊!

看上圖,假設我們要求計算的矩形(注意,這裡是可以推廣到一般情況的,矩形!)是圖示紅色方框所包括的部分(x1,y1)——(x2,y2)。

那麼,這個答案可以通過圖示 褐色方框 - 兩個青色方框 + 黑色方框(由於多減了一次) 得到。

所以, s=

sum[

x2][

y2]−

sum[

x2][

y1−1

]−su

m[x1

−1][

y2]+

sum[

x1−1

][y1

−1]

這就是我們的計算式。

但是,怎麼計算sum呢? su

m[i]

[j]=

sum[

i−1]

[j]+

sum[

i][j

−1]−

sum[

i−1]

[j−1

]+a[

i][j

] 接下來的任務就很簡單了,直接上**

#include 

using namespace std ;

const int maxn = 110 ;

bool a[maxn][maxn] ; // 記錄原始陣列

intsum[maxn][maxn] ; // sum意義同上所述

int main()

}printf ( "%d\n", l ) ;

return

0 ;}

是不是顯然很簡單呢~

例題1:過河【noip 2005 t2】

題目描述

在河上有一座獨木橋,乙隻青蛙想沿著獨木橋從河的一側跳到另一側。在橋上有一些石子,青蛙很討厭踩在這些石子上。由於橋的長度和青蛙一次跳過的距離都是正整數,我們可以把獨木橋上青蛙可能到達的點看成數軸上的一串整點:0,1,……,l(其中l是橋的長度)。座標為0的點表示橋的起點,座標為l的點表示橋的終點。青蛙從橋的起點開始,不停的向終點方向跳躍。一次跳躍的距離是s到t之間的任意正整數(包括s,t)。當青蛙跳到或跳過座標為l的點時,就算青蛙已經跳出了獨木橋。

題目給出獨木橋的長度l,青蛙跳躍的距離範圍s,t,橋上石子的位置。你的任務是確定青蛙要想過河,最少需要踩到的石子數。

輸入輸出格式

輸入格式:

輸入檔案river.in的第一行有乙個正整數l(1 <= l <= 10^9),表示獨木橋的長度。第二行有三個正整數s,t,m,分別表示青蛙一次跳躍的最小距離,最大距離,及橋上石子的個數,其中1 <= s <= t <= 10,1 <= m <= 100。第三行有m個不同的正整數分別表示這m個石子在數軸上的位置(資料保證橋的起點和終點處沒有石子)。所有相鄰的整數之間用乙個空格隔開。

輸出格式:

輸出檔案river.out只包括乙個整數,表示青蛙過河最少需要踩到的石子數。

輸入輸出樣例

輸入樣例#1:

10 2 3 5

2 3 5 6 7

輸出樣例#1:

2 說明

對於30%的資料,l <= 10000;

對於全部的資料,l <= 109。

分析:to be continue … …

低價購買

尼克的任務

多公尺諾骨牌

合唱隊形

奇怪的字串

簡單線性DP總結

首先 動態規劃適用的條件是該問題有最優子結構 無後效性 通過每個最優子結構狀態的遞推可以推出整體的最優解 對這些基本概念的理解既要理性也要一點點感性 dp問題解決順序是 1 確定狀態 通過最後一步將問題轉化為規模更小的子問題 2 轉移方程 3 初始條件與邊界情況 4 計算順序 其中前兩步是十分重要的...

簡單線性回歸演算法

一 目標 尋找一條直線,最大程度的 擬合 樣本特徵和樣本輸出標記之間的關係。在回歸問題中我們 的是乙個具體的數值,這個具體的數值是在乙個連續的空間裡的,如果想看兩個特徵的回歸問題就需要在三維空間裡進行觀察。樣本特徵有多個的回歸稱為多元線性回歸 損失函式 對a求偏導數 最後得到的結果 求a b的pyt...

演算法設計 簡單線性查詢

查詢就是在集合中尋找特定值的過程,線性查詢是查詢演算法中最簡單的演算法,下面介紹原理。我們假設有一張寫滿數字的紙,沒有特定的順序,當要確定數字13是否在這些數字中時,我們會怎麼做?我們可能會從頭到尾的看這些數字,並將每個數字和13進行比較,當在這個過程中發現13的時候,退出並顯示找到了它 如果最後沒...