离散数学:过河问题
過河問題(一)圖論方法
農夫過河問題
問題描述
一個農夫帶著一頭狼、一頭羊、一顆白菜過河。他面前只有一條船,只能容納他和一件物品,只有農夫會劃船。如果農夫不在場,狼會吃羊、羊會吃白菜,農夫在場則不會。求將所有物品運到對岸的方案。
解題思路
根據物品的位置定義狀態,若在左岸記為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 Node10 {11 int u, d; //該節點的編號與距離12 bool operator < (const Node x) const13 {14 return d > x.d;15 }16 };17 18 //邊結構體的定義19 struct Edge20 {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]; //記錄是否被收錄,用來代替集合S31 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 }此類問題很多,但大多可用圖論的思想做(雖然不一定是速度最快的),后續在補充吧,有問題直接留言!
?
過河問題(二)
問題描述
POJ 1700
在一個漆黑的夜里,N為旅行者來到狹窄而沒有護欄的橋邊。如果不借助手電筒的話,大家是無論如何也不敢過橋去的。不幸的是,N個人一共只帶了一只手電筒,而橋窄得只夠讓兩個人同時過。如果各自單獨過橋的話,N人所需要的時間已知;而如果兩人同時過橋,所需要的時間就是走得比較慢的那個人單獨行動時所需的時間。問題是,如何設計一個方案,讓這N人盡快過橋。
解題思路
很容易想到使用貪心策略,但具體怎么做呢?
假設共5個人T1、T2、T3、T4、T5,時間一次增加,如何用最少的時間使得5人都過河?
考慮先將T4和T5過河(最慢的兩個安排好了,無后效性),經過簡單比較,有兩種較優方法。
1、T1和T2過河,T1回來,T4和T5過河,T2回來(總時間:T2 + T1 + T5 + T2)
2、T1和T4過河,T1回來,T1和T5過河,T1回來(總時間:T4 + T1 + T5 + T1)
然后問題簡化為只有3個人T1、T2、T3,n≤3的情況直接觀察。
推廣到n個人(n ≥ 4),只需取(T1 + 2*T2 + Tn)和(2*T1 + Tn-1 + Tn)中的較小者,問題化為前n-2個人的情況。
代碼實現
1 #include<stdio.h>2 #include<iostream>3 #include<algorithm>4 using namespace std;5 6 const int maxn = 1000 + 10;7 int n,times[maxn];8 9 int main() 10 { 11 int T; 12 scanf("%d", &T); 13 while (T--) 14 { 15 scanf("%d", &n); 16 for (int i = 0; i < n; i++) scanf("%d", ×[i]); 17 sort(times, times + n); 18 int sum = 0; 19 while (n > 3) 20 { 21 sum += min((times[0] + times[1] * 2 + times[n - 1]), (times[0] * 2 + times[n - 2] + times[n - 1])); 22 n -= 2; 23 } 24 if (n == 3) sum += (times[0] + times[1] + times[2]); 25 if (n == 2) sum += times[1]; 26 if (n == 1) sum += times[0]; 27 28 printf("%d\n", sum); 29 } 30 return 0; 31 }?參考鏈接:
https://www.cnblogs.com/ShiChaoPeng/p/5858690.html
https://blog.csdn.net/qq_40788630/article/details/79332774?utm_source=blogxgwz1
參考鏈接:中國大學mooc 劉鐸老師? 離散數學
總結
- 上一篇: 统计范围内不含‘7‘的数字个数(洛谷P1
- 下一篇: 【Python】Matplotlib分层