利用動態規劃實現最短路徑和(適合小白看,看不懂你打我,附JS程式碼和C程式碼實現)
要求:利用動態規劃實現最短路徑和。
Example:從起點到終點,只能向右或向上,要求找到最短的路徑。(下圖為了方面大家思考所以標明瞭行列和每段距離,實際情況可能會輸入)
動態規劃思想:
把原始問題分解為一系列子問題
求解每個子問題僅一次,並將結果儲存下來,以後用到時直接取,不重複計算,與此同時,演算法效率提高
自底向上計算
適用:對於一類優化問題,可以分為多個相關子問題,子問題的解被重複使用
(可參考:斐波那契數列,爬樓梯問題)
首先我們標好每個座標(中間的就不標了,太亂了看著)
我們的目的是從(1,1)到(4,5)
我們可以倒著想,如果我要從(4,5)往回走,發現只能走(4,4)或者(3,5)
(4,4)到(4,5)最短路徑就是1,因為沒別的可選
(3,5)到(4,5)最短路徑也是1
我們給(4,4)這裡儲存的值是1,給(3,5)這裡儲存的值是1
然後考慮(3,4)到終點,從(3,4)到(4,5)只有兩條路:
(3,4)->(4,4)->(4,5)
(3,4)->(3,5)->(4,5)
(3,4)->(4,4)->(4,5)的路徑是3+1=4
(3,4)->(3,5)->(4,5)的路徑是8+1=9
所以從(3,4)到(4,5)最短的路徑就是走(3,4)->(4,4)->(4,5),也就是距離為4的是最短路徑和,所以這裡給(3,4)儲存的值是4
相信大家明白啥意思了,我可以定義一個二維陣列,其值儲存從該點到終點最短的路徑和。
就像我從(1,1)算最短路徑和,我只需要知道,(2,1)和(1,2)這兩個分別到終點的路徑和。(因為我們倒著計算,完全能夠確定(2,1)和(1,2)儲存的值是多少,因為他們本身存的值就是最短路徑和)
我怎麼確定從(1,1)走哪條呢?
只需要確定 (1,1)到(2,1)的距離+(2,1)本身儲存的值。(這裡的話也就是3+a[2][1])
以及(1,1)到(1,2)的距離+(1,2)本身儲存的值。(這裡的話也就是5+a[2][1])
我比較這兩個哪個小,我就在(1,1)儲存上計算結果。
即把原始問題分解為一系列子問題,求解每個子問題僅一次,並將結果儲存下來,以後用到時直接取,不重複計算。
如果沒明白,我再舉個例子
我們之前算了a[4][4] =1,a[3][5]=1,a[3][4]=4;
那a[2][5] = ?
因為最右邊比較特殊,只能向上走(同理最上邊只能向右)。即(2,5)->(3,5)->(4,5),然後求一下距離之和。a[2][5]=2+1=3。
懂了嗎?那我們再算一個a[2][4]應該儲存的最短路徑和。
a[2][4]第一步只可以去a[3][4]或a[2][5]。
這時我們不用去考慮從(3,4)或(2,5)之後該怎麼走,因為我們a[3][4]和a[2][5]已經都計算好了怎麼走路徑最短,以及已經儲存好了最短路徑和。
so: 可以有兩個計算方法:
a[2][4] = 2 + a[3][4] = 2+4 = 6;
a[2][4] =4 + a[2][5] = 4+3 = 7;
選最小的,所以,a[2][4] = min(6,7) = 6;
有同學問為什麼不考慮(2,4)->(3,4)->(3,5)->(4,5)
是這樣的:我們從(3,5)到終點的時候我們在計算a[3][5]的時候已經確定了走哪條是最短的,即(3,4)->(4,4)->(4,5)=4的。而非(3,4)->(3,5)->(4,5)的路徑是8+1=9的。
如果大家懂了就快去用程式碼實現一下。
好啦,上程式碼啦(首先是C的):
# include <stdio.h>
struct S{
int up; //該座標向上的距離
int right; //該座標向右的距離
int v; //本身的值
};
int main(void)
{
//m向上 n向右 從(1,1)點到(m,n)
//m、n 可以後期再定義為輸入,這裡指定一下,但以下程式都是用m、m來表示,不會涉及到具體的數。
int m=4,n=5;
struct S a[m+1][n+1];
int i,j;
for(i=1;i<=m;i++)
{
for(j=1;j<=n;j++)
{
//輸入的時候,如果沒有向上或向右的,我們手動輸入 0
scanf("%d",&(a[i][j].up));
scanf("%d",&(a[i][j].right));
}
}
//compute 演算法部分,重點看一下哦!!!
a[m][n].v = 0;
for(i=m;i>0;i--)
{
for(j=n;j>0;j--)
{
if( m==i && j!=n) //當m==i時,可以發現會篩選出最上邊的一行 至於j==n時,算的是終點,終點之前已經賦值了,再計算會混亂
{
a[i][j].v = a[i][j].right + a[i][j+1].v;
}
else if( n==j && i!=m) //當n==j時,可以發現會篩選出最右邊的一行 至於i==m時,算的是終點,終點之前已經賦值了,再計算會混亂
{
a[i][j].v = a[i][j].up + a[i+1][j].v;
}
else if(i!=m && j!=n) //當既不是最上邊也不是最右邊,也不是終點時
{
int n1 = a[i][j].up+a[i+1][j].v;
int n2 = a[i][j].right+a[i][j+1].v;
//要求的是最短路徑 看一下n1和n2哪個小,就儲存哪個
a[i][j].v = n1>n2?n2:n1;
}
}
}
printf("%d",a[1][1].v);
return 0;
}
/*
供測試資料:
3 5
1 6
3 7
1 8
3 0
2 1
2 2
2 3
2 4
2 0
1 5
3 6
1 7
3 8
1 0
0 4
0 3
0 2
0 1
0 0
得出最後結果a[1][1].v=12;
*/
再附上一個JS程式碼:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>WebHomework05</title>
<script type="text/javascript">
//動態規劃實現最小路徑和
//這裡的m和n可以手動輸入,利用prompt語句。這裡指定一下值,但下面的程式用的都是m和n,如果需要修改m和n的值只需要修改定義這裡的就行。
var m = 4;
var n = 5;
/*不太會定義結構體,所以定義了三個二維陣列,基本演算法思想是一樣的*/
//up陣列儲存每個座標的向上的路徑,right儲存每個座標向下的路徑,v陣列利用動態規劃儲存該座標的值到右上終點的最小路徑和
var up = new Array();
for(var i=1;i<=m;i++){
up[i] = new Array();
for(var j=1;j<=n;j++){
up[i][j] = 0;
}
}
var right = new Array();
for(var i=1;i<=m;i++){
right[i] = new Array();
for(var j=1;j<=n;j++){
right[i][j] = 0;
}
}
var v = new Array();
for(var i=1;i<=m;i++){
v[i] = new Array();
for(var j=1;j<=n;j++){
v[i][j] = Number(0);
}
}
//從[1,1]到[m,n]依次輸入所在座標的up和right值
for(var i=1;i<=m;i++){
for(var j=1;j<=n;j++){
up[i][j] = prompt("Please input 'up' a["+i+"]["+j+"]");
right[i][j] = prompt("Please input 'right' a["+i+"]["+j+"]");
}
}
//compute
v[m][n] = Number(0);
for(i=m;i>0;i--)
{
for(j=n;j>0;j--)
{
if( m==i && j!=n)
{
v[i][j] = Number(right[i][j]) + Number(v[i][j+1]);
}
else if( n==j && i!=m)
{
v[i][j] = Number(up[i][j]) + Number(v[i+1][j]);
}
else if(i!=m && j!=n)
{
var n1 = Number(up[i][j]) + Number(v[i+1][j]);
var n2 = Number(right[i][j])+Number(v[i][j+1]);
//要求的是最短路徑,所以三元表示式如下:
v[i][j] = Number(n1>n2?n2:n1);
}
}
}
/*
for(var i=1;i<=m;i++){
for(var j=1;j<=n;j++){
console.log("i="+i+" j="+j+" up="+up[i][j]+" right="+right[i][j]+" v="+v[i][j]);
}
}
//由此迴圈可以觀察出 js將prompt語句所賦的值當成字串來處理了,
//所以我們可以在之前的迴圈賦值語句中呼叫String物件的Number()方法,將字串轉化為數值型
*/
console.log("the min add = "+Number(v[1][1]));
//或者是alert("the min add = "+Number(v[1][1]));
/*供測試的資料:
3 5
1 6
3 7
1 8
3 0
2 1
2 2
2 3
2 4
2 0
1 5
3 6
1 7
3 8
1 0
0 4
0 3
0 2
0 1
0 0
最短路徑和是12,正確,從左下到右上走的分別是3->1->2->2->1->2->1
*/
</script>
</head>
<body>
</body>
</html>
感謝朋友們能看到這裡。
如果程式不懂記得先看懂流程控制,在看懂每個語句的功能,最後試數。
加油呀!
相關文章
- 動態規劃之最短路徑和動態規劃
- PHP使用動態規劃實現最優紅包組合PHP動態規劃
- OutputStreamWriter介紹&程式碼實現和InputStreamReader介紹&程式碼實現
- AES和DES程式碼實現
- Node和http:一本通【附tcp實現http小程式碼】HTTPTCP
- 一道筆試題:利用JS程式碼實現防抖和節流筆試JS
- java 動態規劃(三角形最短路徑和)Java動態規劃
- js 實現程式碼雨效果JS
- 利用 webhook 實現 Git 自動部署 Laravel 程式碼WebHookGitLaravel
- 利用雲函式來實現獲取特定路徑+引數的小程式碼函式
- 利用JavaScript實現註冊頁面省市聯動效果(附程式碼)JavaScript
- mqtt介紹和go程式碼實現MQQTGo
- 小程式頁面動態配置實現
- 前端沙箱利用這些特性實現程式碼的隔離和限制前端
- Python實現快遞分揀小程式(附原始碼和超詳細註釋)Python原始碼
- 詳解動態規劃最長公共子序列--JavaScript實現動態規劃JavaScript
- 5行程式碼實現微信小程式模版訊息推送 (含推送後臺和小程式原始碼)行程微信小程式原始碼
- 使用emscripten實現js直接呼叫C程式碼(emscripten的初探)JSC程式
- 【動態規劃】0-1揹包問題原理和實現動態規劃
- js程式碼實現多人聊天室JS
- Vue專案之使用EditorConfig, Eslint和Prettier實現程式碼規範VueEsLint
- spring+groovy實現動態程式碼注入執行Spring
- Python程式碼實現“FlappyBird”小遊戲PythonAPP遊戲
- UDP內網穿透和打洞原理的C語言程式碼實現UDP內網穿透C語言
- Tomcat程式碼實現Tomcat
- Promise 程式碼實現Promise
- BIO、NIO、AIO區別(看不懂你打我)AI
- 最短路徑——dijkstra演算法程式碼(c語言)演算法C語言
- 最短路徑——floyd演算法程式碼(c語言)演算法C語言
- 微信小程式功能之全屏滾動效果的實現程式碼微信小程式
- 10行程式碼實現微信小程式支付功能,使用小程式雲開發實現小程式支付功能(行程微信小程式
- 【微信小程式canvas】實現小程式手寫板使用者簽名(附程式碼)微信小程式Canvas
- Locust 程式碼指令碼實現指令碼
- 使用PHP實現動態代理IP的示例程式碼PHP
- 藉助 Webpack 靜態分析能力實現程式碼動態載入Web
- html5 canvas 實現光線沿不規則路徑運動例項程式碼HTMLCanvas
- FTP客戶端c程式碼功能實現FTP客戶端C程式
- Linklist程式碼實現以及程式碼解讀