動態規劃學習筆記

aCssen發表於2024-08-03

動態規劃(dynamic programing)

動態規劃是什麼?

是運籌學的一個分支,是求解決策過程最優化的數學方法。

為什麼要用動態規劃?

動態規劃利用各個階段之間的關係,逐個求解,最終求得全域性最優解。

怎麼用動態規劃?

1,確認原問題與子問題

2,動態規劃狀態

3,邊界狀態

4,邊界狀態結值

5,狀態轉移方程***

由於博主是蒟蒻,所以dp那麼多好用的內容要一點點更新,希望本菜雞堅持更完! 

例題

P1048 採藥(01揹包模板題)

題目描述

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

如果你是辰辰,你能完成這個任務嗎?

輸入格式

第一行有 22 個整數 TT(1 \le T \le 10001≤T≤1000)和 MM(1 \le M \le 1001≤M≤100),用一個空格隔開,TT 代表總共能夠用來採藥的時間,MM 代表山洞裡的草藥的數目。

接下來的 MM 行每行包括兩個在 11 到 100100 之間(包括 11 和 100100)的整數,分別表示採摘某株草藥的時間和這株草藥的價值。

輸出格式

輸出在規定的時間內可以採到的草藥的最大總價值。

輸入輸出樣例

輸入 #1複製

70 3
71 100
69 1
1 2

輸出 #1複製

3

說明/提示

  • 對於 30\%30% 的資料,M \le 10M≤10;
  • 對於全部的資料,M \le 100M≤100。

NOIP2005 普及組 第三題

原題連結:https://www.luogu.com.cn/problem/P1048

#include<bits/stdc++.h>
using namespace std;
int t,m,dp[1010],w[110],v[110];
int main()
{
	ios::sync_with_stdio(false);
	cin>>t>>m;
	for(int i=1;i<=m;i++) cin>>w[i]>>v[i];
	for(int i=1;i<=m;i++)
	{
		for(int j=t;j>=w[i];j--)//和完全揹包的不同 
		dp[j]=max(dp[j-w[i]]+v[i],dp[j]);
	}
	cout<<dp[t];
	return 0;
}

採藥的題解有一篇特別良心,博主好像是衡水的?連結:https://nmslqwq.blog.luogu.org/solution-p1048 

 P1616 瘋狂的採藥(完全揹包模板題)

題目描述

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

如果你是 LiYuxiang,你能完成這個任務嗎?

此題和原題的不同點:

11. 每種草藥可以無限制地瘋狂採摘。

22. 藥的種類眼花繚亂,採藥時間好長好長啊!師傅等得菊花都謝了!

輸入格式

輸入第一行有兩個整數,分別代表總共能夠用來採藥的時間 tt 和代表山洞裡的草藥的數目 mm。

第 22 到第 (m + 1)(m+1) 行,每行兩個整數,第 (i + 1)(i+1) 行的整數 a_i, b_iai​,bi​ 分別表示採摘第 ii 種草藥的時間和該草藥的價值。

輸出格式

輸出一行,這一行只包含一個整數,表示在規定的時間內,可以採到的草藥的最大總價值。

輸入輸出樣例

輸入 #1複製

70 3
71 100
69 1
1 2

輸出 #1複製

140

說明/提示

資料規模與約定

  • 對於 30\%30% 的資料,保證 m \le 10^3m≤103 。
  • 對於 100\%100% 的資料,保證 1 \leq m \le 10^41≤m≤104,1 \leq t \leq 10^71≤t≤107,且 1 \leq m \times t \leq 10^71≤m×t≤107,1 \leq a_i, b_i \leq 10^41≤ai​,bi​≤104
  • 原題連結:https://www.luogu.com.cn/problem/P1616

#include<bits/stdc++.h>
using namespace std;
const int maxm=10010,maxn=10000010;
long long v[maxm],w[maxm],dp[maxn];
int main()
{
	ios::sync_with_stdio(false);
	int t,m;
	cin>>t>>m;
	for(int i=1;i<=m;i++) cin>>w[i]>>v[i];
	for(int i=1;i<=m;i++){
	for(int j=w[i];j<=t;j++)//保證可以重複取,區分01揹包 
	dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
	}
	cout<<dp[t];
	return 0;
}

P1434 [SHOI2002]滑雪 

題目描述

Michael 喜歡滑雪。這並不奇怪,因為滑雪的確很刺激。可是為了獲得速度,滑的區域必須向下傾斜,而且當你滑到坡底,你不得不再次走上坡或者等待升降機來載你。Michael 想知道在一個區域中最長的滑坡。區域由一個二維陣列給出。陣列的每個數字代表點的高度。下面是一個例子:

5 5
1   2   3   4   5
16  17  18  19  6
15  24  25  20  7
14  23  22  21  8
13  12  11  10  9

一個人可以從某個點滑向上下左右相鄰四個點之一,當且僅當高度會減小。在上面的例子中,一條可行的滑坡為 2424-1717-1616-11(從 2424 開始,在 11 結束)。當然 2525-2424-2323-\ldots…-33-22-11 更長。事實上,這是最長的一條。

輸入格式

輸入的第一行為表示區域的二維陣列的行數 RR 和列數 CC。下面是 RR 行,每行有 CC 個數,代表高度(兩個數字之間用 11 個空格間隔)。

輸出格式

輸出區域中最長滑坡的長度。

輸入輸出樣例

輸入 #1複製

5 5
1 2 3 4 5
16 17 18 19 6
15 24 25 20 7
14 23 22 21 8
13 12 11 10 9

輸出 #1複製

25

說明/提示

對於 100\%100% 的資料,1\leq R,C\leq 1001≤R,C≤100。

原題連結:https://www.luogu.com.cn/problem/P1434

#include<bits/stdc++.h>
using namespace std;
int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
int n,m,ans;
int a[201][201],s[201][201];
int dfs(int x,int y)
{
	if(s[x][y])return s[x][y];//記憶化搜尋 
	s[x][y]=1;
	for(int i=0;i<4;i++)
	{
		int xx=dx[i]+x;
		int yy=dy[i]+y;// 用於遍歷四個方向 
		if(xx>0&&yy>0&&xx<=n&&yy<=m&&a[x][y]>a[xx][yy]){
			dfs(xx,yy);
			s[x][y]=max(s[x][y],s[xx][yy]+1);//記憶化 疑問:為什麼是s[xx][yy]加1? 
		}
	}
	return s[x][y];
}
int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
		cin>>a[i][j];
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
		ans=max(ans,dfs(i,j));//取最大值 
	cout<<ans<<endl;
	return 0;
			
}

 

 

相關文章