農夫過河問題
問題描述
一個農夫帶着一頭狼、一頭羊、一顆白菜過河。他面前只有一條船,只能容納他和一件物品,只有農夫會划船。如果農夫不在場,狼會吃羊、羊會吃白菜,農夫在場則不會。求將所有物品運到對岸的方案。
解題思路
根據物品的位置定義狀態,若在左岸記為1,右岸記為0,於是最終方案就是(1,1,1,1)-->(0,0,0,0)所經過的路徑。
1、定義狀態
2、列舉所有狀態(人、狼、羊、菜)
3、刪除不合理的狀態(狼和羊、羊和菜)
4、連邊(模擬一次渡河)
5、尋找路徑
尋找(1111)-->(0000)的邊,可以用尋路算法如bfs、dfs,如果要求最短路可以用最短路算法如bfs、Dijsktra等,當然這里圖很簡單,可直接觀察出來。
(1111)-->(0101)-->(1101)-->(0001)-->(1011)-->(0010)-->(1010)-->(0000)(兩條最短路之一) 左岸 右岸 1、人 狼 羊 花 空 2、狼 花 人 羊 3、人 狼 花 羊 4、花 人 狼 羊 5、人 羊 花 狼 6、羊 人 花 狼 7、人 羊 狼 花 8、空 狼 花 人 羊
傳教士與吃人惡魔的問題
問題描述
有三個傳教士和三個吃人惡魔要渡過一條河,河中有一條船,只能裝下兩個人。在任何地方(無論是岸邊還是船上),如果吃人惡魔數量多於傳教士數量,吃人惡魔就會吃掉傳教士。問:怎么才能讓這些都安全過河?
解題思路
1、定義狀態
2、列舉所有狀態
3、刪除不合理狀態
4、連邊(模擬依次渡河變化)
5、尋找路徑
尋找(33 L 00)-->(00 R 33)的路徑
其中一條路徑 (33 L 00)-->(31 R 01)-->(32 L 01)-->(30 R 03)-->(31 L 02)-->(11 R 22)-->(22 L 01)-->(02 R 31)-->(03 L 30)-->(01 R 32)-->(02 L 31)-->(00 R 33) 1、兩個吃人惡魔過河 2、一個吃人惡魔回來 3、兩個吃人惡魔過河 4、一個吃人惡魔回來 5、兩個傳教士過河 6、一個傳教士和一個吃人惡魔回來 7、兩個傳教士回來 8、一個吃人惡魔回去 9、兩個吃人惡魔過河 10、一個吃人惡魔回去 11、兩個吃人惡魔過河
四人過橋問題
問題描述
在一個漆黑的夜里,四位旅游者來到一座狹窄而沒有護欄的橋邊,如果不借助手電筒的話,大家是無論也不敢過去。不幸的是四個人中只有一只手電筒,而橋窄得只夠兩個人同時通過。如果各自單獨過橋得話,四個人所需要的時間分別是1、2、5、10分鍾,如果兩個人同時過橋,所需要的時間是較慢的那個人單獨行動時的時間。問:如何設計一個方案,讓四個人盡快過橋。
解題思路
與前面兩個相比,這次不僅要求方案,同時要求時間最短。
同樣需要定義狀態,四個人+手電筒的位置
1、定義狀態
2、建圖
分為每次通過一個人和每次兩個人,都是帶權無向邊。
(下面只連接了與(01111)的邊)
3、尋找最短路
尋找(L 1111)-->(R 0000)的最短路,即最短路算法中(01111)-->(10000)的最短路,以下是利用Dijstra算法的解決方法。
最終答案為(2 + 1 + 10 + 2 + 2) = 17.
1 #include<stdio.h> 2 #include<iostream> 3 #include<string> 4 #include<queue> 5 #include<algorithm> 6 using namespace std; 7 8 //定義圖中結點 9 struct Node 10 { 11 int u, d; //該節點的編號與距離 12 bool operator < (const Node x) const 13 { 14 return d > x.d; 15 } 16 }; 17 18 //邊結構體的定義 19 struct Edge 20 { 21 int to; 22 int w; 23 int next; 24 }; 25 26 const int INF = 0x3f3f3f3f; 27 const int V = 32 + 10; 28 const int E = 32 * 32 + 10; 29 int dis[V]; //源到各頂點的最短距離 30 int vis[V]; //記錄是否被收錄,用來代替集合S 31 int head[V]; //head[i]表示頂點i的第一條邊的數組下標,"-1"表示頂點i沒有邊 32 Edge edge[E]; 33 34 inline void AddEdge(int a, int b, int w, int id) 35 { 36 edge[id].to = b; 37 edge[id].w = w; 38 edge[id].next = head[a]; 39 head[a] = id; 40 return; 41 } 42 43 //s為起點 44 void Dijsktra(int s) 45 { 46 priority_queue<Node>q; //取出集合T中的最小值 47 memset(vis, 0, sizeof(vis)); 48 memset(dis, INF, sizeof(dis)); 49 50 dis[s] = 0; 51 q.push(Node{ s, dis[s] }); 52 while (!q.empty()) 53 { 54 Node x = q.top(); q.pop(); 55 int u = x.u; 56 57 if (vis[u]) continue; 58 59 vis[u] = true; 60 for (int i = head[u]; i != -1; i = edge[i].next) //松弛與u直接相鄰的頂點 61 { 62 int v = edge[i].to; 63 int w = edge[i].w; 64 if (!vis[v] && dis[u] + w < dis[v]) 65 { 66 dis[v] = dis[u] + w; 67 q.push(Node{ v,dis[v] }); 68 } 69 } 70 } 71 } 72 73 const int score[] = { 1,2,5,10 }; //每個人單獨行動的時間 74 75 int main() 76 { 77 //建圖 78 memset(head, -1, sizeof(head)); 79 int id = 0; 80 for (int i = 0; i < (1 << 4); i++) 81 { 82 int bits[4]; 83 for (int j = 0; j < 4; j++) bits[j] = (i >> j) & 1; 84 //一次走一個人 85 for (int j = 0; j < 4; j++) if (bits[j]) 86 { 87 int tmp = i - (1 << j) + 16; 88 int w = score[j]; 89 AddEdge(i, tmp, w, id++); 90 AddEdge(tmp, i, w, id++); 91 } 92 //一次走兩個人 93 for(int j = 0;j < 3;j++) 94 for (int k = j + 1; k < 4; k++) if (bits[j] && bits[k]) 95 { 96 int tmp = i - (1 << j) - (1 << k) + 16; 97 int w = max(score[j],score[k]); 98 AddEdge(i, tmp, w, id++); 99 AddEdge(tmp, i, w, id++); 100 } 101 } 102 Dijsktra(15); 103 printf("%d\n", dis[16]); 104 105 return 0; 106 }
此類問題很多,但大多可用圖論的思想做(雖然不一定是速度最快的),后續在補充吧,有問題直接留言!
參考鏈接:中國大學mooc 劉鐸老師 離散數學