离散数学:过河问题

过河问题(一)图论方法

农夫过河问题

问题描述

一个农夫带着一头狼、一头羊、一颗白菜过河。他面前只有一条船,只能容纳他和一件物品,只有农夫会划船。如果农夫不在场,狼会吃羊、羊会吃白菜,农夫在场则不会。求将所有物品运到对岸的方案。

解题思路

根据物品的位置定义状态,若在左岸记为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 }

复制代码

此类问题很多,但大多可用图论的思想做(虽然不一定是速度最快的),后续在补充吧,有问题直接留言!

 

过河问题(二)

问题描述

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", &times[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 刘铎老师  离散数学

  • 4
    点赞
  • 48
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 离散数学是一门研究离散结构、离散数量以及离散关系的数学学科。在计算机科学、信息技术和其他相关领域中,离散数学被广泛应用于解决各种问题。 屈婉玲是离散数学领域的著名教授,她的教材可能会是一本介绍离散数学的教材。一本好的离散数学教材能够提供深入浅出的理论知识和实践应用,帮助学生理解和运用离散数学的基本概念和技巧。 PDF格式的教材是一种便捷的电子书格式,它可以在各种设备上阅读、搜索和打印。离散数学屈婉玲教材的PDF版本可能可以供学生、教师和学术研究人员下载和使用。 通过阅读离散数学屈婉玲教材的PDF版本,学生可以学习离散数学的基本概念,如集合论、逻辑、图论、概率论等,并且了解离散数学在计算机科学与信息技术中的应用。教材可能提供了大量的例题和习题,帮助学生理解和巩固所学内容。 总的来说,离散数学屈婉玲教材的PDF版本可能是一本有助于学习离散数学的教材,它可以提供理论知识和实践应用,帮助学生掌握离散数学的基本概念和技巧。 ### 回答2: 离散数学是一门研究离散结构的数学学科。离散数学的教材有很多种,其中屈婉玲教材是其中之一。这本教材是由屈婉玲编写的,主要面向离散数学的初学者。 屈婉玲教材pdf的出现为学习离散数学的人提供了便利。通过电子版教材,学习者可以随时随地地阅读和学习。这样可以更加灵活地安排学习时间和地点,提高学习效率。此外,电子版教材还可以方便地进行搜索和标注,便于回顾和复习。 离散数学是计算机科学、信息技术等领域的基础学科,具有很高的实用价值。掌握离散数学的知识可以帮助我们理解计算机程序的基本原理和逻辑,有助于解决实际问题和开展科学研究。屈婉玲教材pdf的存在,方便了学习者获取相关知识,提高学习效果。 总之,离散数学屈婉玲教材pdf的出现为学习者提供了便利,帮助他们更好地学习离散数学的知识。通过学习离散数学,我们可以掌握一种科学的思维方式,提高问题分析和解决问题的能力,为未来的学习和工作打下坚实的基础。 ### 回答3: 离散数学是计算机科学和数学的一个重要分支,广泛用于计算机科学中的算法、数据结构、图论和逻辑等领域。屈婉玲是离散数学领域的一位著名教授,她所编写的离散数学教材PDF非常有价值。 屈婉玲教授的教材涵盖了离散数学的各个方面,内容丰富深入,既适合作为离散数学的教材使用,也适合作为离散数学的参考书研究。教材包含了离散数学的基本概念、原理和定理,以及相关的证明和推导过程。而且,教材还提供了丰富的例题和练习题,可以帮助读者巩固所学的知识和提高解题能力。 离散数学教材PDF的优点在于它的便捷性和免费性。使用PDF格式的教材可以随时随地在电子设备上进行阅读,无需携带纸质书籍。而且,这本教材是免费提供的,大家可以自行下载和使用。 总之,离散数学屈婉玲教材PDF是一本非常有价值的教材,对于学习和研究离散数学都具有重要意义。无论是作为离散数学课程的教材,还是作为离散数学领域的参考书,这本教材都能为读者提供丰富的知识和资源。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值