動態規劃,顯然是乙個很讓人頭疼的地方,也沒有個固定的演算法,最多就是有一些模板(比如揹包啊),要是想要增大做出來的機率,也就只好多做做題找找感覺了~
線性動態規劃可以說是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的時候,退出並顯示找到了它 如果最後沒...