国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

動(dòng)態(tài)規(guī)劃_打家劫舍(Ⅰ~Ⅲ)

這篇具有很好參考價(jià)值的文章主要介紹了動(dòng)態(tài)規(guī)劃_打家劫舍(Ⅰ~Ⅲ)。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問(wèn)。


前言

打家劫舍系列


198. 打家劫舍

1_動(dòng)態(tài)規(guī)劃

返回最大金額
不能同時(shí)取相鄰兩個(gè)數(shù)
數(shù)組數(shù)據(jù)全部非負(fù)

①dp數(shù)組含義

dp[i]表示前i個(gè)數(shù)中按規(guī)則取出的最大總和

②遞推公式
dp[i]=max(dp[i-1],dp[i-2]+nums[i])

當(dāng)前最優(yōu)可以從兩個(gè)狀態(tài)推出(前提是前面已經(jīng)為最優(yōu)解):

1° 前一個(gè)數(shù)未?。簞t當(dāng)前數(shù)取了,則總和最大

2° 前一個(gè)數(shù)已?。罕容^不考慮當(dāng)前數(shù)字的最大總和 (dp[i] ) 以及不考慮上一個(gè)數(shù)字的最大總和加上當(dāng)前數(shù)字(dp[i-2]+nums[i]因?yàn)楫?dāng)上一個(gè)數(shù)字未取時(shí),才可以取當(dāng)前數(shù)字

③dp數(shù)組初始化

因?yàn)檫f推公式中用到了i-2項(xiàng)
所以遍歷時(shí)要從dp數(shù)組的第2項(xiàng)開(kāi)始遍歷,防止數(shù)組越界
所以要初始化dp數(shù)組第2項(xiàng)前面的所有項(xiàng)

dp[0]和dp[1]可以手推出來(lái)0&nums[0]

④遍歷順序

當(dāng)前最優(yōu)由已知最優(yōu)推出,應(yīng)正序遍歷

完整代碼:

int rob(vector<int>& nums) {
	// 定義dp數(shù)組&初始化為0
		// dp數(shù)組含義:在前i個(gè)數(shù)中按規(guī)則選擇后的最大和
	vector<int> dp(nums.size() + 1, 0);
	
	// 因?yàn)檫f推公式會(huì)用到i-2,為保證不越界,初始化到1,并從2開(kāi)始遍歷
	// dp[0]沒(méi)有可選的數(shù)字---0
	dp[0] = 0;
	// dp[1]選上唯一一個(gè)數(shù)則為最大---nums[0]
	dp[1] = nums[0];

	// 遍歷dp數(shù)組
	for (int i = 2; i < dp.size(); i++)
		dp[i] = max(dp[i - 1], dp[i - 2] + nums[i-1]);
	// 返回答案
	return dp[nums.size()];
}

2_優(yōu)化空間復(fù)雜度

由遞推公式可以看出來(lái),當(dāng)前項(xiàng)只需要用到前兩項(xiàng)就可以推出
所以:

不用將dp數(shù)組長(zhǎng)度開(kāi)為nums數(shù)組的長(zhǎng)度
用兩個(gè)變量記錄前兩項(xiàng)的值
在遍歷時(shí)更新

	// 兩個(gè)變量記錄
	// dp[i-2]
	int ppre = 0;
	// dp[i-1]
	int pre = nums[0];
	// dp[i]---初始化為nums[0],當(dāng)nums長(zhǎng)度為1時(shí)范圍首項(xiàng)
	int cur = nums[0];
	// 遍歷nums數(shù)組
	for (int i = 2; i <= nums.size();i++) {
		cur = max(pre, ppre + nums[i-1]);
		ppre = pre;
		pre = cur;
	}
	// 返回答案
	return cur;

213. 打家劫舍 II

1_動(dòng)態(tài)規(guī)劃

在打家劫舍Ⅰ的基礎(chǔ)上加上了條件:

當(dāng)?shù)谝婚g房子被偷過(guò)時(shí),最后一間房子就不能偷
只有第一間房子,沒(méi)被偷過(guò)時(shí),最后一間房子才能偷

分(含頭不含尾&含尾不含頭)兩段區(qū)間分別計(jì)算各自的最大值

動(dòng)規(guī)五部曲和打家劫舍Ⅰ大致相同
需要注意:

int rob(vector<int>& nums) {
	// nums數(shù)組長(zhǎng)度為1時(shí)返回第一項(xiàng)
	if (nums.size() == 1)
		return nums[0];
	
	//  定義dp數(shù)組
	vector<int> dp(nums.size());
	
	// 包含首索引---------------------
	// 初始化0,1項(xiàng)
	dp[0] = 0;
	dp[1] = nums[0];
	for (int i = 2; i < dp.size(); i++)
		dp[i] = max(dp[i - 1], dp[i - 2] + nums[i - 1]);
	int m1 = dp[dp.size() - 1];

	// 包含尾索引----------------------
	// 初始化1項(xiàng)為2項(xiàng)(間接刪除首索引)
	dp[1] = nums[1];
	for (int i = 2; i < dp.size(); i++)
		dp[i] = max(dp[i - 1], dp[i - 2] + nums[i]);

	return max(m1, dp[dp.size() - 1]);
}

2_優(yōu)化空間復(fù)雜度

int rob(vector<int>& nums) {
	// 變量記錄法
	if (nums.size() == 1)
		return nums[0];
	// 含頭不含尾----------------------
	// dp[i-2]
	int ppre = 0;
	// dp[i-1]
	int pre = nums[0];
	// dp[i]
	int cur1 = nums[0];
	// 遍歷nums數(shù)組
	for (int i = 2; i < nums.size(); i++) {
		cur1 = max(pre, ppre + nums[i - 1]);
		ppre = pre;
		pre = cur1;
	}

	// 含尾不含頭
	ppre = 0;
	pre = nums[1];
	int cur2 = nums[1];
	for (int i = 3; i <= nums.size(); i++) {
		cur2 = max(pre, ppre + nums[i - 1]);
		ppre = pre;
		pre = cur2;
	}

	// 返回兩個(gè)區(qū)間里的較大者
	return max(cur1, cur2);
}

337. 打家劫舍 III

0_復(fù)習(xí)樹(shù)

因?yàn)楦鷺?shù)有關(guān),我貼一個(gè)遍歷方法出來(lái):

遞歸法遍歷樹(shù)適用于各種

樹(shù)節(jié)點(diǎn)結(jié)構(gòu)體定義:

struct TreeNode {
	int val;
	TreeNode* left;
	TreeNode* right;
	TreeNode() : val(0), left(nullptr), right(nullptr) {}
	TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
	TreeNode(int x, TreeNode* left, TreeNode* right) : val(x), left(left), right(right) {}
};

根節(jié)點(diǎn)的構(gòu)造和函數(shù)遍歷函數(shù)調(diào)用:

int main() {
	// [3,2,3,null,3,null,1]
	TreeNode* root = new TreeNode(3, new TreeNode(2, nullptr, new TreeNode(3)), new TreeNode(3, nullptr, new TreeNode(1)));
	postTraval(root);
	return 0;
}

遞歸法的后序遍歷

void postTraval(TreeNode* root) {
	if (root == nullptr)
		return;
	// 左右中
	if (root->left != nullptr)
		postTraval(root->left);
	if (root->right != nullptr)
		postTraval(root->right);
	cout << root->val << " ";
}

迭代法的后續(xù)遍歷:

// 后序遍歷_迭代版
void postTraval(TreeNode* root) {
	/*
	棧
		先存入后訪問(wèn)
	空指針標(biāo)記
		取節(jié)點(diǎn)時(shí),判斷是否為空指針
			空指針:取出的是需要訪問(wèn)的節(jié)點(diǎn)
			非空指針:加上空指針標(biāo)記,便于后續(xù)取出訪問(wèn)
	*/
	// 數(shù)組做棧,存放遍歷節(jié)點(diǎn)
	TreeNode* stack[30] = { 0 };
	// 棧頂指針
	int top = -1;
	// 預(yù)先存入根節(jié)點(diǎn)
	stack[++top] = root;
	// 迭代父節(jié)點(diǎn)---當(dāng)棧不為空時(shí),還有節(jié)點(diǎn)尚未遍歷
	while (top != -1) {
			// 取出節(jié)點(diǎn)
		TreeNode* cur = stack[top--];
		if (cur != nullptr) {
			// 存入節(jié)點(diǎn)---后序遍歷(左右中)---棧的后入先出---(中右左)
				// 存入當(dāng)前節(jié)點(diǎn)時(shí),需要加入標(biāo)記---先入節(jié)點(diǎn)在入空指針
			stack[++top] = cur;
			stack[++top] = nullptr;
			// 存左子節(jié)點(diǎn)
			if (cur->right != nullptr)
				stack[++top] = cur->right;
			// 存右子節(jié)點(diǎn)
			if (cur->left != nullptr)
				stack[++top] = cur->left;
		}
		else {
			// 進(jìn)行訪問(wèn)---因?yàn)槿〕龅氖强罩羔?,下一個(gè)棧頂才是要訪問(wèn)的節(jié)點(diǎn)
			cur = stack[top--];
			cout << cur->val << " ";
		}
	}
}

1_動(dòng)態(tài)規(guī)劃

本題里第一次涉及到樹(shù)狀dp

樹(shù)狀dp和數(shù)組dp只是數(shù)據(jù)結(jié)構(gòu)不同
原理都是在遍歷數(shù)據(jù)時(shí)進(jìn)行狀態(tài)轉(zhuǎn)移
注意狀態(tài)轉(zhuǎn)移的方向
當(dāng)選擇后序遍歷樹(shù)時(shí),因?yàn)槭窍冗M(jìn)入左右子節(jié)點(diǎn),所以當(dāng)前狀態(tài)才能從子狀態(tài)中推出

①dp值含義

考慮當(dāng)前節(jié)點(diǎn)及其所有子節(jié)點(diǎn)后,得到的最大值

②遞推公式

選擇兩種情況中的最大值

1° 選擇當(dāng)前節(jié)點(diǎn),遞歸進(jìn)入孫子節(jié)點(diǎn)
val1=當(dāng)前節(jié)點(diǎn)值+孫子節(jié)點(diǎn)的最大值

2° 不選擇當(dāng)前節(jié)點(diǎn),遞歸進(jìn)入子節(jié)點(diǎn)
val2=左右子節(jié)點(diǎn)的最大值

	// 偷當(dāng)前節(jié)點(diǎn)---遞歸進(jìn)入孫子節(jié)點(diǎn)
	int val1 = root->val;
		// 加入判斷,避免操作空指針
	if (root->left != nullptr)
		val1 += rob(root->left->left) + rob(root->left->right);
	if (root->right != nullptr)
		val1 += rob(root->right->left) + rob(root->right->right);

	// 不偷當(dāng)前節(jié)點(diǎn)---遞歸進(jìn)入子節(jié)點(diǎn)
	int val2 = rob(root->left) + rob(root->right);

因?yàn)樽訝顟B(tài)直接由各個(gè)節(jié)點(diǎn)值可以直接得出,所以不用初始化

④遍歷順序

選擇后序遍歷樹(shù)
因?yàn)槭?strong>先進(jìn)入左右子節(jié)點(diǎn),所以當(dāng)前狀態(tài)才能從子狀態(tài)中推出

完整代碼:

int rob(TreeNode* root) {
	// 當(dāng)傳入節(jié)點(diǎn)為空時(shí),返回0
	if (root == nullptr)
		return 0;
	// 傳入節(jié)點(diǎn)非空且無(wú)子節(jié)點(diǎn)時(shí),返回當(dāng)前節(jié)點(diǎn)值
	if (root->left == nullptr && root->right == nullptr)
		return root->val;

	// 偷當(dāng)前節(jié)點(diǎn)---遞歸進(jìn)入孫子節(jié)點(diǎn)
	int val1 = root->val;
		// 加入判斷,避免操作空指針
	if (root->left != nullptr)
		val1 += rob(root->left->left) + rob(root->left->right);
	if (root->right != nullptr)
		val1 += rob(root->right->left) + rob(root->right->right);

	// 不偷當(dāng)前節(jié)點(diǎn)---遞歸進(jìn)入子節(jié)點(diǎn)
	int val2 = rob(root->left) + rob(root->right);

	// 在兩種情況中返回較大值
	return max(val1, val2);
}

2_記憶化搜索(備忘錄)

提交會(huì)發(fā)現(xiàn)有測(cè)試用例超時(shí),因?yàn)榇嬖诖罅恐貜?fù)計(jì)算

例如:在不考慮當(dāng)前節(jié)點(diǎn)時(shí),進(jìn)入的左右子節(jié)點(diǎn)可能已經(jīng)通過(guò)之前的孫子節(jié)點(diǎn)計(jì)算過(guò)結(jié)果
所以可以通過(guò)設(shè)置一個(gè)數(shù)據(jù)結(jié)構(gòu)來(lái)記錄每一個(gè)節(jié)點(diǎn)的計(jì)算結(jié)果

數(shù)據(jù)結(jié)構(gòu)的選擇

本題中我們要選擇一個(gè)能夠存儲(chǔ)樹(shù)節(jié)點(diǎn),并且能夠通過(guò)樹(shù)節(jié)點(diǎn)查詢(xún)到其計(jì)算結(jié)果的數(shù)據(jù)結(jié)構(gòu)
自然會(huì)想到使用map(通過(guò)鍵查詢(xún)值)
因?yàn)橥ㄟ^(guò)遞歸遍歷樹(shù),所以不能選擇在某一層建立map,應(yīng)該開(kāi)全局map
而平時(shí)是數(shù)組,僅需通過(guò)索引查詢(xún),用數(shù)組做備忘錄就行

完整代碼:

unordered_map<TreeNode*, int> visit;
int rob(TreeNode* root) {
	// 當(dāng)傳入節(jié)點(diǎn)為空時(shí),返回0
	if (root == nullptr)
		return 0;
	// 傳入節(jié)點(diǎn)非空且無(wú)子節(jié)點(diǎn)時(shí),返回當(dāng)前節(jié)點(diǎn)值
	if (root->left == nullptr && root->right == nullptr)
		return root->val;
	
	// 在選擇偷法前判斷是否已經(jīng)存在計(jì)算結(jié)果
	if (visit.find(root) != visit.end())
		return visit[root];
		// ②find函數(shù)返回迭代器,通過(guò)second獲取值
		// return visit.find(root)->second;
		
	// 偷當(dāng)前節(jié)點(diǎn)---遞歸進(jìn)入孫子節(jié)點(diǎn)
	int val1 = root->val;
		// 加入判斷,避免操作空指針
	if (root->left != nullptr)
		val1 += rob(root->left->left) + rob(root->left->right);
	if (root->right != nullptr)
		val1 += rob(root->right->left) + rob(root->right->right);

	// 不偷當(dāng)前節(jié)點(diǎn)---遞歸進(jìn)入子節(jié)點(diǎn)
	int val2 = rob(root->left) + rob(root->right);

	// 返回前存入結(jié)果
	visit[root]=max(val1,val2);
		// 插入鍵值對(duì)
		// visit.insert(pair<TreeNode*,int>(root,max(val1,val2);
	// 在兩種情況中返回較大值
	return max(val1, val2);
}

總結(jié)

打家劫舍的dp遞推公式比較容易找到
難的是邊界情況判斷
以及跟樹(shù)結(jié)合時(shí)需要使用備忘錄文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-773622.html

到了這里,關(guān)于動(dòng)態(tài)規(guī)劃_打家劫舍(Ⅰ~Ⅲ)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來(lái)自互聯(lián)網(wǎng)用戶(hù)投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 【算法|動(dòng)態(tài)規(guī)劃No.10】leetcode LCR 089. 打家劫舍 & LCR 090. 打家劫舍 II

    【算法|動(dòng)態(tài)規(guī)劃No.10】leetcode LCR 089. 打家劫舍 & LCR 090. 打家劫舍 II

    個(gè)人主頁(yè):兜里有顆棉花糖 歡迎 點(diǎn)贊?? 收藏? 留言? 加關(guān)注??本文由 兜里有顆棉花糖 原創(chuàng) 收錄于專(zhuān)欄【手撕算法系列專(zhuān)欄】【LeetCode】 ??本專(zhuān)欄旨在提高自己算法能力的同時(shí),記錄一下自己的學(xué)習(xí)過(guò)程,希望對(duì)大家有所幫助 ??希望我們一起努力、成長(zhǎng),共同進(jìn)步。

    2024年01月20日
    瀏覽(22)
  • leetcode-打家劫舍專(zhuān)題系列(動(dòng)態(tài)規(guī)劃)

    leetcode-打家劫舍專(zhuān)題系列(動(dòng)態(tài)規(guī)劃)

    你是一個(gè)專(zhuān)業(yè)的小偷,計(jì)劃偷竊沿街的房屋。每間房?jī)?nèi)都藏有一定的現(xiàn)金,影響你偷竊的唯一制約因素就是相鄰的房屋裝有相互連通的防盜系統(tǒng),如果兩間相鄰的房屋在同一晚上被小偷闖入,系統(tǒng)會(huì)自動(dòng)報(bào)警。 給定一個(gè)代表每個(gè)房屋存放金額的非負(fù)整數(shù)數(shù)組,計(jì)算你 不觸動(dòng)

    2024年04月14日
    瀏覽(24)
  • 力扣198. 打家劫舍(java 動(dòng)態(tài)規(guī)劃)

    力扣198. 打家劫舍(java 動(dòng)態(tài)規(guī)劃)

    Problem: 198. 打家劫舍 1.構(gòu)建多階段決策模型:n個(gè)房屋對(duì)應(yīng)n個(gè)階段,每一個(gè)階段決定一個(gè)房間是偷還是不偷,兩種決策:偷、不偷 2.定義狀態(tài):不能記錄每個(gè)階段決策完之后,小偷可偷的最大金額,需要記錄不同決策對(duì)應(yīng)的最大金額,也就是:這個(gè)房屋偷-對(duì)應(yīng)的最大金額;這

    2024年01月21日
    瀏覽(24)
  • 【學(xué)會(huì)動(dòng)態(tài)規(guī)劃】打家劫舍 II(12)

    【學(xué)會(huì)動(dòng)態(tài)規(guī)劃】打家劫舍 II(12)

    目錄 動(dòng)態(tài)規(guī)劃怎么學(xué)? 1. 題目解析 2. 算法原理 1. 狀態(tài)表示 2. 狀態(tài)轉(zhuǎn)移方程 3. 初始化 4. 填表順序 5. 返回值 3. 代碼編寫(xiě) 寫(xiě)在最后: 學(xué)習(xí)一個(gè)算法沒(méi)有捷徑,更何況是學(xué)習(xí)動(dòng)態(tài)規(guī)劃, 跟我一起刷動(dòng)態(tài)規(guī)劃算法題,一起學(xué)會(huì)動(dòng)態(tài)規(guī)劃! 題目鏈接:213. 打家劫舍 II - 力扣(Lee

    2024年02月15日
    瀏覽(22)
  • 動(dòng)態(tài)規(guī)劃-經(jīng)典dp(打家劫舍,股票等)

    動(dòng)態(tài)規(guī)劃-經(jīng)典dp(打家劫舍,股票等)

    1.1.1 爬樓梯 ?由于求的是組合數(shù),我們將不同路徑相加即可 dp定義: dp[i]為爬到第i階樓梯的方法數(shù); 轉(zhuǎn)移方程: 初始化: ?由于涉及到i-2和i-1,那么我們要從i=2開(kāi)始遍歷,因此要初始化dp[0] = 0,dp[1] = 1(根據(jù)定義) 遍歷順序: 從左往右? 完整代碼: ?1.1.2?使用最小花費(fèi)爬樓梯

    2024年01月19日
    瀏覽(26)
  • 【LeetCode熱題100】198. 打家劫舍(動(dòng)態(tài)規(guī)劃)

    你是一個(gè)專(zhuān)業(yè)的小偷,計(jì)劃偷竊沿街的房屋。每間房?jī)?nèi)都藏有一定的現(xiàn)金,影響你偷竊的唯一制約因素就是相鄰的房屋裝有相互連通的防盜系統(tǒng), 如果兩間相鄰的房屋在同一晚上被小偷闖入,系統(tǒng)會(huì)自動(dòng)報(bào)警。 給定一個(gè)代表每個(gè)房屋存放金額的非負(fù)整數(shù)數(shù)組,計(jì)算你 不觸動(dòng)

    2024年04月11日
    瀏覽(27)
  • leetcode 343.整數(shù)拆分 198.打家劫舍(動(dòng)態(tài)規(guī)劃)

    leetcode 343.整數(shù)拆分 198.打家劫舍(動(dòng)態(tài)規(guī)劃)

    ? ?OJ鏈接 :leetcode 343.整數(shù)拆分 代碼:? OJ鏈接 :198.打家劫舍 ? ?代碼:

    2024年02月05日
    瀏覽(38)
  • 動(dòng)態(tài)規(guī)劃day09(打家劫舍,樹(shù)形dp)

    動(dòng)態(tài)規(guī)劃day09(打家劫舍,樹(shù)形dp)

    目錄 198.打家劫舍 看到題目的第一想法 看到代碼隨想錄之后的想法 自己實(shí)現(xiàn)過(guò)程中遇到的困難 213.打家劫舍II 看到題目的第一想法 看到代碼隨想錄之后的想法 自己實(shí)現(xiàn)過(guò)程中遇到的困難 337.打家劫舍 III(樹(shù)形dp) 看到題目的第一想法 看到代碼隨想錄之后的想法 自己實(shí)現(xiàn)過(guò)程中

    2024年01月23日
    瀏覽(23)
  • Day 42 算法記錄|動(dòng)態(tài)規(guī)劃 09 (打家劫舍)

    Day 42 算法記錄|動(dòng)態(tài)規(guī)劃 09 (打家劫舍)

    1.dp[i]:考慮下標(biāo)i(包括i)以?xún)?nèi)的房屋,最多可以偷竊的金額為dp[i]。 2.dp[i] = max(dp[i - 2] + nums[i], dp[i - 1]); 3.初始化,dp[0] 和 dp[1],dp[0] 一定是 nums[0],dp[1] = max(nums[0], nums[1]); 3.遍歷順序,dp[i] 是根據(jù)dp[i - 2] 和 dp[i - 1] 推導(dǎo)出來(lái)的,那么一定是從前到后遍歷! 進(jìn)一步對(duì)滾動(dòng)數(shù)組

    2024年02月15日
    瀏覽(24)
  • Java 動(dòng)態(tài)規(guī)劃 Leetcode 213. 打家劫舍 II

    Java 動(dòng)態(tài)規(guī)劃 Leetcode 213. 打家劫舍 II

    代碼展示: ????????該題其實(shí)是Java 動(dòng)態(tài)規(guī)劃 面試題 17.16. 按摩師的變種,增加了一個(gè)首尾是相鄰的條件,而我們解決該題也要用到鏈接的這道題的思想,可以先去看一下上面這篇博客 此題可以采用動(dòng)態(tài)規(guī)劃的方法進(jìn)行解決,根據(jù)解決動(dòng)態(tài)規(guī)劃題目的5大步驟進(jìn)行逐步分析

    2024年02月13日
    瀏覽(24)

覺(jué)得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包