前言

前段时间才做了离散实验的实验报告,为防止在本地遗失,所以上传到CSDN上一份,欢迎大家一起学习

实验1 关系元算

实验报告内容

一、实验目的

熟悉掌握命题逻辑中的联结词、真值表、主范式等,进一步能用他们来解决实际问题。

二、实验内容

1、从键盘输入两个命题变元P和Q的真值,求它们的合取、析取、条件和双条件的真值。(A)

2、求任意一个命题公式的真值表(B,并根据真值表求主范式(C))

三、实验环境

C或C++语言编程环境实现。
DEVC++ 5.4.0
MinGW GCC 4.7.2 32-bit
-std=c++17

四、实验原理和过程(算法)

A:直接if……else……判断,没啥好说的。

B:没有考虑括号和时间效率就简单多了。考虑括号的话最优解应该是用树+栈(貌似是中值表达式的模板题),考虑到只有五个优先级,就直接用map存下五个优先级,然后从高到低暴力遍历,每次把结果算出来化简字符串,最后输出字符创就是真值表最终的值。

C:相比于B来说,C就简单多了,考虑一下一共有多少个不同的命题,然后以此为位数,直接枚举+位运算,感觉没啥要注意的点。

五、程序清单

A:

#include <bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
const int N = 1e6 + 100;
const int mod = 1e6 + 3;
int dp[500][500];
int s[500][500];
int a[N];
bool judge1(int p, int q)
{if (p == 1){if (q == 1)return 1;}return 0;
} //合取
bool judge2(int p, int q)
{if (p == 0){if (q == 0)return 0;}return 1;
} //析取
bool judge3(int p, int q)
{if (p == 1){if (q == 0)return 0;}return 1;
} //条件
bool judge4(int p, int q)
{if (judge3(p, q)){if (judge3(q, p))return 1;}return 0;
} //双条件
bool judge(int p, int q)
{if (p == 1){if (q == 1)return 1;if (q == 0)return 1;return 0;}if (p == 0){if (q == 1)return 1;if (q == 0)return 1;return 0;}return 0;
}
signed main()
{int p, q;cout << "P:";cin >> p;cout << "Q:";cin >> q;if (judge(p, q) == 0){cout << "error\n";return 0;}cout << "析取:" << judge2(p, q) << endl;cout << "合取:" << judge1(p, q) << endl;cout << "条件:" << judge3(p, q) << endl;cout << "双条件:" << judge4(p, q) << endl;
}

B1暴力做法:(只能计算三个命题之间的真值表,无法判断括号)

#include <bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
map<int, char> mp;
bool judge1(int p)
{return (p + 1) % 2;
} //非
bool judge2(int p, int q)
{if (p == 1){if (q == 1){return 1;}}return 0;
} //且
bool judge3(int p, int q)
{if (p == 0){if (q == 0){return 0;}}return 1;
} //并
bool judge4(int p, int q)
{if (p == 1){if (q == 0){return 0;}}return 1;
} //条件
bool judge5(int p, int q)
{if (p == 1){if (q == 1){return 1;}return 0;}if (p == 0){if (q == 0){return 1;}return 0;}
} //双条件
void solve(int i, int j, int k, string x)
{while (x.find('i') != string::npos){x[x.find('i')] = i + '0';}while (x.find('j') != string::npos){x[x.find('j')] = j + '0';}while (x.find('k') != string::npos){x[x.find('k')] = k + '0';} //替换未知数为值for (int l = 5; l > 0; l--){for (int r = 0; r < x.length(); r++){if (x[r] == mp[l]){if (l == 5){cout << judge1(x[r + 1] - '0') << " ";x[r] = judge1(x[r + 1] - '0') + '0';x.erase(r + 1, 1);} //判断‘!’,并将计算结果替换原始表达式if (l == 4){cout << judge2(x[r - 1] - '0', x[r + 1] - '0') << " ";x[r - 1] = judge2(x[r - 1] - '0', x[r + 1] - '0') + '0';x.erase(r, 2);} //判断‘^’,并将计算结果替换原始表达式if (l == 3){cout << judge3(x[r - 1] - '0', x[r + 1] - '0') << " ";x[r - 1] = judge3(x[r - 1] - '0', x[r + 1] - '0') + '0';x.erase(r, 2);} //判断‘v’,并将计算结果替换原始表达式if (l == 2){cout << judge4(x[r - 1] - '0', x[r + 1] - '0') << " ";x[r - 1] = judge4(x[r - 1] - '0', x[r + 1] - '0') + '0';x.erase(r, 2);} //判断‘->’,并将计算结果替换原始表达式if (l == 1){cout << judge5(x[r - 1] - '0', x[r + 1] - '0') << " ";x[r - 1] = judge5(x[r - 1] - '0', x[r + 1] - '0') + '0';x.erase(r, 2);} //判断‘<->’,并将计算结果替换原始表达式r--;}}}cout << x << endl; //化简到最后只剩一个字符,即输出一个数字
}
bool judge_ele(string y, int i)
{if (y[i] != 'P'){if (y[i] != 'Q'){if (y[i] != 'R'){return 0;}}}return 1;
} //判断字母
signed main()
{string x, y;cin >> x;mp[5] = '!';mp[4] = '^';mp[3] = 'v';mp[2] = '#';mp[1] = '@';    // mp记录五种运算的优先级y = "!^v#@PQR"; //合法字符串模板while (x.find("<->") != string::npos){int p = x.find("<->");x[p] = '@';x.erase(p + 1, 2);}while (x.find("->") != string::npos){int p = x.find("->");x[x.find("->")] = '#';x.erase(p + 1, 1);} //将多字节运算符替换为单字节for (int i = 0; x[i]; i++){if (count(y.begin(), y.end(), x[i]) == 0){cout << "error!\n";return 0;}} //判断输入符号是否合法string temp = x;for (int i = 0; i < 2; i++){for (int j = 0; temp[j]; j++){if (i == 0){if (temp[j] == '!'){if (judge_ele(temp, j + 1) == 0){cout << "error!\n";return 0;}}} // i==0时优先判断!是否合法(即观察!后是否为合法字母)else{if (judge_ele(temp, j) == 0){if (j - 1 < 0 && judge_ele(temp, j - 1) == 0){cout << "error!\n";return 0;}else{if (j + 1 >= temp.length() && judge_ele(temp, j + 1) == 0){cout << "error!\n";return 0;}}} //当前位置不是字母时,判断该运算符前后是否为合法字母,若不是合法字母,则输出errorelse{if (j - 1 < 0 && judge_ele(temp, j - 1)){cout << "error!\n";return 0;}else{if (j + 1 >= temp.length() && judge_ele(temp, j + 1)){cout << "error!\n";return 0;}}} //当前位置为合法字母时,判断字母前后是否为运算符 ,若不是合法运算符,则输出error}}while (temp.find("!") != string::npos){int p = temp.find("!");temp.erase(p, 1);} //当‘!’判断完后,删除所有的‘!’以防影响下一次判断}     //判断表达式是否合法while (x.find('P') != string::npos){x[x.find('P')] = 'i';}while (x.find('Q') != string::npos){x[x.find('Q')] = 'j';}while (x.find('R') != string::npos){x[x.find('R')] = 'k';} //将'P','Q','R'替换成'i','j','k'与枚举变量名字一致,方便表示for (int i = 0; i <= 1; i++){for (int j = 0; j <= 1; j++){for (int k = 0; k <= 1; k++){cout << i << " " << j << " " << k << " ";solve(i, j, k, x); //枚举每一种取值情况}}}
}

B2语法树做法:(可以判断括号,对命题数量无限制)

#include<bits/stdc++.h>
#define OP 0
#define NUM 1//宏定义确定该位置为操作符还是数字
using namespace std;
set<char>operator_set = { '(', ')', '=', '^', '!', '&', '|' };//操作集
map<char, int>level; //优先级
bool judge1(int p) {return (p + 1) % 2;
} //非
bool judge2(int p, int q) {if (p == 1) {if (q == 1) {return 1;}}return 0;
} //且
bool judge3(int p, int q) {if (p == 0) {if (q == 0) {return 0;}}return 1;
} //并
bool judge4(int p, int q) {if (p == 1) {if (q == 0) {return 0;}}return 1;
} //条件
bool judge5(int p, int q) {if (p == 1) {if (q == 1) {return 1;}return 0;}if (p == 0) {if (q == 0) {return 1;}return 0;}
} //双条件
class Token {public:int num;//数字(常量)char op;//操作符int type;Token(char ch) {type = OP;op = ch;}Token(int x) {type = NUM;num = x;}
};
class TreeNode {public:int x;char op;int type;//记录读入类型TreeNode *L;TreeNode *R;//左右子节点TreeNode() {};TreeNode(TreeNode *_L, char _op, TreeNode *_R) {type = OP;op = _op;L = _L, R = _R;}TreeNode(int _x) {type = NUM;x = _x;}//构造函数int eval() {//递归计算if (type == NUM)return x;//当检查类型为数时,已到节点底端,直接返回值switch (op) {case '|':return judge3(L->eval(), R->eval());case '&':return judge2(L->eval(), R->eval());case '!':return judge1(R->eval());case '@':return judge5(L->eval(), R->eval());case '#':return judge4(L->eval(), R->eval());}//未到底端继续递归往下查找子树计算结果}
};
stack<TreeNode*>num_stk;
stack<char>op_stk;
void addnode() {TreeNode *R = num_stk.top();//右节点为栈顶元素num_stk.pop();TreeNode *L = new TreeNode(0);//定义新节点为0char op = op_stk.top();op_stk.pop();if (op == '!') {num_stk.push(new TreeNode(L, op, R));//若为!直接入栈} else {if (num_stk.size())L = num_stk.top(), num_stk.pop();num_stk.push(new TreeNode(L, op, R));//其他则再取左节点为另一栈顶元素,入栈}
}
int Expression(string s) {level['!'] = 5;level['&'] = 4;level['|'] = 3;level['#'] = 2;level['@'] = 1;level['('] = level[')'] = 0;//定义优先级while (num_stk.size()) num_stk.pop();while (op_stk.size()) op_stk.pop();//清空栈vector<Token>v;for (auto it : s) {if (it == '1' || it == '0') {v.push_back(Token(it - '0'));} else {v.push_back(Token(it));}}//开辟数组,并将读入字符串转化为数字与操作符的组合for (auto t : v) {if (t.type == NUM) {num_stk.push(new TreeNode(t.num));//建立语法树} else {switch (t.op) {case '(':op_stk.push('(');break;case ')':while (op_stk.size() && op_stk.top() != '(')addnode();op_stk.pop();//当有后括号时查找前括号break;default:while (op_stk.size() && level[op_stk.top()] >= level[t.op])addnode();op_stk.push(t.op);//先判断待插入操作与栈顶优先级,再加点并插入}}}while (op_stk.size())addnode();//查看操作栈是否为空,若不为空则运算未结束,继续加点return num_stk.top()->eval();
}signed main() {string s, y;y = "ABCDEFGHIJKLMNOPQRSTUVWXYZ#@!&|()";int idx = 0;cin >> s;while (s.find("<->") != string::npos) {int p = s.find("<->");s[p] = '@';s.erase(p + 1, 2);}while (s.find("->") != string::npos) {int p = s.find("->");s[s.find("->")] = '#';s.erase(p + 1, 1);} //将多字节运算符替换为单字节while (s.find("^") != string::npos) {int p = s.find("^");s[s.find("^")] = '&';}//替换v为&while (s.find("v") != string::npos) {int p = s.find("v");s[s.find("v")] = '|';}//替换^为|for (int i = 0; s[i]; i++) {if (count(y.begin(), y.end(), s[i]) == 0) {cout << "error!\n";return 0;}} //判断输入符号是否合法set<char>st;map<char, int>mp;for (int i = 0; s[i]; i++) {if (s[i] <= 'Z' && s[i] >= 'A') {st.insert(s[i]);mp[s[i]] = 1;}}//将输入命题按从小到大排序for (auto it = mp.begin() ; it != mp.end(); it++) it->second = idx++;//给命题编号int num1 = 0, num0 = st.size();//枚举,从全0无1开始for (auto it : mp) cout << it.first << ' ';//输出表头cout << "ans" << endl;//输出表头最后一列while (num1 <= st.size()) {vector<int>temp;for (int i = 1; i <= num0; i++) temp.push_back(0);for (int i = 1; i <= num1; i++) temp.push_back(1);//按枚举数量分别放入相应数量0,1do {//全排列枚举该数量下0,1位置string x;x = s;for (int it = 0; x[it]; it++) {if (x[it] <= 'Z' && x[it] >= 'A') {x[it] = temp[mp[x[it]]] + '0';}}//将对应位置命题符号转化为01for (auto it : temp) cout << it << ' ';//输出命题取值cout << Expression(x) << endl;//输出真值} while (next_permutation(temp.begin(), temp.end()));num0--;num1++;}
}

C:

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e5 + 100;
int cnt[N];
signed main()
{int m;bool f = 0;cin >> m;int s = 0;for (int i = 0; i < pow(2, m); i++){cin >> cnt[i]; // cnt存储真值表最后一列if (cnt[i] != 1){if (cnt[i] != 0)f = 1; //判断输入是否合法(是否有除0,1外的数)}}if (f){cout << "error!\n";return 0;}cout << "主析取范式:";for (int i = 0; i < pow(2, m); i++){int t = m;  //获取位数if (cnt[i]) //当命题为真时进入{int k = i;if (s != 0){if (i != pow(2, m))cout << "v"; //若不是首元,输出‘v’}s++;cout << "(";while (t){int j = (k >> (t - 1)) & 1; //位运算判断当前为情况该位置命题为‘1’还是为‘0’if (j == 0)cout << "!"; //若为0输出!cout << (char)('P' + m - t);if (t != 1)cout << "^";t--;}cout << ")";}} //主析取范式cout << endl;s = 0;cout << "主合取范式:";for (int i = 0; i < pow(2, m); i++){int t = m;   //获取位数if (!cnt[i]) //当命题为假时进入{int k = i;if (s){if (i != pow(2, m))cout << "^"; //若不是首元,输出‘^’}s++;cout << "(";while (t){int j = (k >> (t - 1)) & 1; //位运算判断当前为情况该位置命题为‘1’还是为‘0’if (j == 1)cout << "!"; //若为1输出!cout << (char)('P' + m - t);if (t != 1)cout << "v";t--;}cout << ")";}} //主合取范式cout << endl;
}

实验2 集合运算与操作

实验报告内容

一、实验目的

集合论是一切数学的基础,也是计算机科学不可或缺的,在数据结构、数据库理论、开关理论、自动机理论和可计算理论等领域都有广泛的应用。集合的运算规则是集合论中的重要内容。通过该组实验,目的是让学生更加深刻地理解集合的概念和性质,并掌握集合的运算规则等。

二、实验内容

(1)求任意两个集合的交集、并集、差集。
(2)求任意一个集合的幂集。
(3)求任意一个集合的所有 m 元子集。
(4)求任意个元素的全排列。

三、实验环境

C或C++语言编程环境实现。
DEVC++ 5.4.0
MinGW GCC 4.7.2 32-bit
-std=c++17

四、实验原理和过程(算法)

关键词
DFS 位运算
集合的表示采用列举法,如 A={a,b,c,d}。
(1)求任意两个集合的交集、并集、差集。
A∩B={x|x ∈ \in ∈ A∧x ∈ \in ∈B}
A∪B={x|x ∈ \in ∈A∨x ∈ \in ∈B}
A-B={x|x ∈ \in ∈A∧x ∉ \notin ∈/​ B}
(2)求任意一个集合的幂集。
P(A)={Ai|i ∈ \in ∈J},其中 J={i|i 是二进制数且000 ⋯ \cdots ⋯ 0 ≤ \leq ≤ i ≤ \leq ≤ 111 ⋯ \cdots ⋯ 1}。
(3)求任意一个集合的所有 m 元子集。
按照(2)求出子集并判断是否符合要求。
(4)求任意个元素的全排列。
设 S={1,2,3,…,n},(a1,a2, ⋯ \cdots ⋯,an)和(b1,b2, ⋯ \cdots ⋯,bn)是 S 的两个全排列,若存在 i ∈ \in ∈{1,2, ⋯ \cdots ⋯,n},使得对一切 j=1,2, ⋯ \cdots ⋯,i 有 aj=bj且 ai+1<bi+1,则称排列(a1,a2, ⋯ \cdots ⋯,an)字典序的小于(b1,b2, ⋯ \cdots ⋯,bn)。记为(a1,a2, ⋯ \cdots ⋯,an)<(b1,b2, ⋯ \cdots ⋯,bn)。若(a1,a2, ⋯ \cdots ⋯,an)<(b1,b2, ⋯ \cdots ⋯,bn),且不存在(c1,c2, ⋯ \cdots ⋯,cn)使得(a1,a2, ⋯ \cdots ⋯,an)< (c1,c2, ⋯ \cdots ⋯,cn)<(b1,b2, ⋯ \cdots ⋯,bn),则称(b1,b2, ⋯ \cdots ⋯,bn)为(a1,a2, ⋯ \cdots ⋯,an)的下一个排列。
求一个排列(a1,a2, ⋯ \cdots ⋯,an)的下一个排列的算法如下:
(1)求满足关系式 aj-1<aj的 j 的最大值,设为 i,即 i=max{j|aj-1<aj}
(2)求满足关系式 ai-1<ak的 k 的最大值,设为 j,即 j=max{k|ai-1<ak}
(3)ai-1与 aj互换得序列(b1,b2, ⋯ \cdots ⋯,bn)
(4)将(b1,b2, ⋯ \cdots ⋯,bn)中部分 bi,bi+1,…,bn 的顺序逆转,得到(b1,b2, ⋯ \cdots ⋯,bi-1,bn, ⋯ \cdots ⋯,bi)便是所求得下一个排列

五、程序清单

(1)求任意两个集合的交集、并集、差集。

代码

#include <bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
const int N = 1e6 + 100;
const int mod = 1e9 + 7;
int qpow(int a, int b)
{int ans = 1, base = a;while (b){if (b & 1)ans = ans * base;base = base * base;b >>= 1;}return ans;
}void or_set(vector<int> a, vector<int> b) //交集
{cout << "交集:";bool f = 0; //若遍历完A仍为0,则意味着交集为空集for (auto it : a){if (*lower_bound(b.begin(), b.end(), it) == it)cout << it << " ", f = 1; //二分查找该元素是否在b中出现过}if (!f)cout << "Φ";cout << endl;
}void and_set(vector<int> a, vector<int> b) //并集
{cout << "并集:";vector<int> c;bool f = 0; //判断是否为空集for (auto it : a){if (*lower_bound(b.begin(), b.end(), it) == it)b.erase(lower_bound(b.begin(), b.end(), it)); //找到相同元素则删除防止重复c.push_back(it);}for (auto it : b){c.push_back(it); // b中剩余元素即为B-A,压入c中}sort(c.begin(), c.end());for (auto it : c){cout << it << " ";f = 1;}if (!f)cout << "Φ"; //判断是否为空集cout << endl;
}void cha_set(vector<int> a, vector<int> b) //差集
{cout << "差集:\n";cout << "A-B:"; //先判断A-Bvector<int> d;bool f = 0;for (auto it : a){if (*lower_bound(b.begin(), b.end(), it) != it)d.push_back(it); //若A中含有B中不含有则进入d数组}sort(d.begin(), d.end()); //排序for (auto it : d){cout << it << " "; //只要d数组不为空,即为有元素,f变为1,否则输出空集f = 1;}if (!f)cout << "Φ";cout << endl;cout << "B-A:"; //判断B-Ad.clear();        //清空d数组f = 0;          //初始化ffor (auto it : b){if (*lower_bound(a.begin(), a.end(), it) != it)d.push_back(it); //若B中含有A中不含有则进入d数组}sort(d.begin(), d.end()); //排序for (auto it : d){cout << it << " "; //只要d数组不为空,即为有元素,f变为1,否则输出空集f = 1;}if (!f)cout << "Φ";cout << endl;
}signed main()
{int temp;vector<int> a, b;cout << "集合A:\n";map<int, int> mp1, mp2;int p, q;cout << "集合A元素个数:";cin >> p;while (p--){cin >> temp;if (mp1[temp])continue; //判断输入元素是否有重复,重复则不再进入数组else{mp1[temp] = 1; //标记该元素已进入数组a.push_back(temp);}}cout << "集合B:\n";cout << "集合B元素个数:";cin >> q;while (q--){cin >> temp;if (mp2[temp])continue; //判断输入元素是否有重复,重复则不再进入数组else{mp2[temp] = 1; //标记该元素已进入数组b.push_back(temp);}}sort(a.begin(), a.end()); //排序,方便后续查找sort(b.begin(), b.end()); //排序,方便后续查找and_set(a, b);or_set(a, b);cha_set(a, b);
}

(2)求任意一个集合的幂集。

代码

#include <bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
const int N = 1e6 + 100;
const int mod = 1e9 + 7;
int qpow(int a, int b)
{ //快速幂,保障精度int ans = 1, base = a;while (b){if (b & 1)ans = ans * base;base = base * base;b >>= 1;}return ans;
}signed main()
{int temp;vector<int> a, b;cout << "集合A:\n";map<int, int> mp1;int p, q;cout << "集合A元素个数:";cin >> p;while (p--){cin >> temp;if (mp1[temp])continue; //判断输入元素是否有重复,重复则不再进入数组else{mp1[temp] = 1; //标记该元素已进入数组a.push_back(temp);}}q = a.size(); //由于数组大小有可能因为判重改变,必须重新更新数组大小sort(a.begin(), a.end());for (int i = 0; i < qpow(2, q); i++) //类似于第一次实验C的真值表写法,用位运算判断当前位数是否该取{int k = q; //记录位数while (k){if ((i >> (k - 1)) & 1)cout << a[k - 1] << " "; //若当前位为1则输出,否则不输出k--;}if (i == 0)cout << "Φ"; // i==0时全为0,即为空集cout << endl;}
}

(3)求任意一个集合的所有 m 元子集。

代码

#include <bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
const int N = 1e6 + 100;
const int mod = 1e9 + 7;
int qpow(int a, int b)
{ //快速幂,保障精度int ans = 1, base = a;while (b){if (b & 1)ans = ans * base;base = base * base;b >>= 1;}return ans;
}signed main()
{int temp;vector<int> a, b;cout << "集合A:\n";map<int, int> mp1;int p, q;cout << "集合A元素个数:";cin >> p;while (p--){cin >> temp;if (mp1[temp])continue; //判断输入元素是否有重复,重复则不再进入数组else{mp1[temp] = 1; //标记该元素已进入数组a.push_back(temp);}}q = a.size(); //由于数组大小有可能因为判重改变,必须重新更新数组大小int m;cout << "m个数:\n";cin >> m; //记录m元子集中m的大小sort(a.begin(), a.end());for (int i = 0; i < qpow(2, q); i++) //几乎和B题一样{int k = q; //多判断一次子集个数是否为m,若不为m则不输出int j = q;int t = 0;while (k){if ((i >> (k - 1)) & 1)t++;k--;}         //判断子集元素个数if (t == m) //判断是否为m元子集{while (j){if ((i >> (j - 1)) & 1)cout << a[j - 1] << " ";j--;} //输出if (i == 0)cout << "Φ"; // i==0时全为0,即为空集cout << endl;}}
}

(4)求任意个元素的全排列。

代码

#include <bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
const int N = 1e6 + 100;
const int mod = 1e9 + 7;void dfs(int start, int end, vector<int> a)
{if (start >= end) //判断递归层数,若到底,输出并返回{for (auto it : a){cout << it << " ";}cout << endl;return;}for (int i = start; i <= end; i++){swap(a[start], a[i]);   //交换目标位和末位dfs(start + 1, end, a); //按此新顺序递归,固定i,并dfs从i+1层开始swap(a[start], a[i]);    //换回,防止排序顺序混乱}
}signed main()
{int temp;vector<int> a, b;cout << "集合A:\n";map<int, int> mp1;int p, q;cout << "集合A元素个数:";cin >> p;while (p--){cin >> temp;if (mp1[temp])continue; //判断输入元素是否有重复,重复则不再进入数组else{mp1[temp] = 1; //标记该元素已进入数组a.push_back(temp);}}q = a.size(); //由于数组大小有可能因为判重改变,必须重新更新数组大小sort(a.begin(), a.end());dfs(0, q - 1, a); // dfs全排序
}

实验3 最短路径

实验报告内容

一、实验目的

图论是一个应用十分广泛而又极其有趣的数学分支,以离散对象的二元关系结构为研究
对象。图论在网络理论、信息论、控制论和计算机科学等领域都有着广泛的应用。通过该组
实验,目的是让学生更加深刻地理解图论中的一些基本概念,并熟悉图在计算机上的表示和
运算方法,且能够解决实际图论问题。

二、实验内容

在简单图中求源点 a 到其它各点(b,c,…,j)的所有最短路径。

三、实验环境

C或C++语言编程环境实现。
DEVC++ 5.4.0
MinGW GCC 4.7.2 32-bit
-std=c++17

四、实验原理和过程(算法)

五、程序清单

A1(dijkstra朴素版):

#include<bits/stdc++.h>
#define int long long
#define endl '\n'
#define IOS ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
using namespace std;
const int N = 1e6 + 100;
const int MAXN = 0x3f3f3f3f3f3f3f3f;
int n, m, start;
int mp[1005][1005];
int dist[N];
int st[N];
void dijkstra() {memset(dist, 0x3f, sizeof dist);//初始化为无穷大memset(st, 0, sizeof st);//初始化dist[start] = 0;//将起点设置距离为0for (int i = 1; i < n; i++) {//由于最多n个节点,若有路,则最多走n-1次int var, d = MAXN;for (int j = 1; j <= n; j++) {if (!st[j] && d > dist[j]) {d = dist[j];var = j;}}//找到当前位置未确定,且距离最短的点st[var] = 1;//将该点确定for (int j = 1; j <= n; j++) {if (!st[j] && mp[var][j]) {dist[j] = min(dist[j], dist[var] + mp[var][j]);}//用距离最小点去更新所有还未确定点的距离}}
}
signed main() {IOS;cin >> n >> m >> start;//输入结点数,边数,以及起点while (m--){int i, j, w;cin >> i >> j >> w;//输入边(起点,终点,权值)if (mp[i][j] != 0)//若有重边,根据贪心思想取权值最小的边{mp[i][j] = min(mp[i][j], w);mp[j][i] = min(mp[j][i], w);//无向图等效为双向边,故把起始点和终点倒置即可}else//若无重边直接赋值{mp[i][j] = w;mp[j][i] = w;}}dijkstra();for (int i = 1; i <= n; i++) {cout << i << ':';//第i个点if (dist[i] == MAXN) cout << -1 << endl;//若无路则输出-1else cout << dist[i] << endl;//有路则输出路径}
}

A2(dijkstra堆优化版本,复杂度更小):

#include <bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N = 1e6 + 100;
const int mod = 1e9 + 7;
typedef pair<int, int> PII;
int mp[1005][1005];
int dist[N];
int st[N];
int n, m, start;
void dijkstra()//经典dijkstra算法复杂度为O(n^2),本代码实现的是其堆优化,复杂度更加优秀为O(mlogn)
{memset(dist, 0x3f, sizeof dist);//初始化为无穷大memset(st, 0, sizeof st);//初始化dist[start] = 0;priority_queue<PII, vector<PII>, greater<PII>> heap;//stl默认为大根堆,这里要用小根堆heap.push({0, start});//将起点入堆while (heap.size())//当堆为空结束{auto t = heap.top();//由于小根堆特性,改点一定为路径最小的点heap.pop();//堆顶元素赋值后出堆int ver = t.second, distance = t.first;if (st[ver]) continue;//若改点已经确定,则一定为最短路,不需要再次判断st[ver] = 1;//将本次访问的点确定,以此点为起始点入堆查找最短路为下一次访问for (int j = 1; j <= n; j++){if (mp[ver][j] != 0 && st[j] == 0)//若遍历的该点有路且该点未确定,则进行判断{if (dist[j] > distance + mp[ver][j])//若未确定的点已得路径小于从新结点到此点的路径,则不改变,反之则改变{dist[j] = distance + mp[ver][j];heap.push({dist[j], j});//路径更新,则需要重新入堆}}}}
}
signed main()
{cin >> n >> m >> start;//输入结点数,边数,以及起点while (m--){int i, j, w;cin >> i >> j >> w;//输入边(起点,终点,权值)if (mp[i][j] != 0)//若有重边,根据贪心思想取权值最小的边{mp[i][j] = min(mp[i][j], w);mp[j][i] = min(mp[j][i], w);//无向图等效为双向边,故把起始点和终点倒置即可}else//若无重边直接赋值{mp[i][j] = w;mp[j][i] = w;}}dijkstra();for (int i = 1; i <= n; i++)//输出起点到所有点的最短路{if (dist[i] == 0x3f3f3f3f3f3f3f3f)//若为无穷大则无路cout << - 1 << " ";elsecout << dist[i] << " ";}//这样输出第n行就是起点到第n个节点的最短路,若无路则输出-1
}

离散数学实验三则(关系元算,集合运算与操作,最短路)相关推荐

  1. 离散实验偏序关系满足实验报告C语言,离散数学实验三

    <离散数学实验三>由会员分享,可在线阅读,更多相关<离散数学实验三(6页珍藏版)>请在人人文库网上搜索. 1.实 验 报 告(2014 / 2015 学年 第 一 学期)课程名 ...

  2. 离散实验偏序关系满足实验报告C语言,离散数学实验三:偏序关系中盖住关系的求取及格论中有补格的判定...

    离散数学实验报告 我希望我能骄傲的走下去!大家每天都要开心向上哦! 题目:输入n,求1~n 中的满足整除关系的因子.再根据盖住关系的原理求盖住关系.最后判断是否为有补格.任意输入一个整数作为n 值. ...

  3. 离散数学实验笛卡尔乘积C语言,集合的运算和笛卡尔乘积

    <集合的运算和笛卡尔乘积>由会员分享,可在线阅读,更多相关<集合的运算和笛卡尔乘积(9页珍藏版)>请在人人文库网上搜索. 1.大连民族学院计算机科学与工程学院实验报告实验题目: ...

  4. 数据库原理实验 三个关系表 学生表students 课程表course 学生选课表sc

    实验数据如下: 在数据库中建立三个关系表Students,Course,SC. 学生表:Student(Sno,Sname,Ssex,Sage,Sdept) 课程表:Course(Cno,Cname, ...

  5. 离散数学——第三章 关系

    文章目录 第三章 关系 3.1.1.本章概述 3.2.关系 3.2.1关系的概念 3.3.2.关系的性质 3.3.3.关系的组成与复合 3.2.4.关系的表示 3.3.闭包(Closure) 3.3. ...

  6. 南京邮电大学离散数学实验三:编程实现整除关系这一偏序关系上所有盖住关系的求取,并判定对应偏序集是否为格

    实验原理及内容 说明:这部分内容主要包括: 1.形式化描述实验中所使用的数据结构和存储结构,给出函数之间的调用关系和数据传递方式: 2.给出核心算法的C++或Java等语言的源代码,并加上详细注释,分 ...

  7. 南京邮电大学离散数学实验三(传递性,自反性,对称性)

    public class Math {private int[] factor = new int[100];// 存数的所有因子private int count = 0; // 因子计数器priv ...

  8. C语言求幺元的函数,离散数学实验指导书及其答案.doc

    实验一 命题逻辑公式化简 [实验目的]加深对五个基本联结词(否定.合取.析取.条件.双条件)的理解.掌握利用基本等价公式化简公式的方法. [实验内容]用化简命题逻辑公式的方法设计一个表决开关电路. 实 ...

  9. 离散数学实验题目-集合

    离散数学实验报告 计算机科学与技术系 目录 第一章 实验概述 3 1.1 实验目的 3 1.2 实验内容 3 1.3 实验环境 3 第二章 实验原理和实现过程 4 2.1 实验原理 4 2.2 实验过 ...

最新文章

  1. Mac OS X的快捷键
  2. 软件测试相关好文收集
  3. QML基础类型之georectangle
  4. html switch开关实现隐藏,css3实现switch开关效果
  5. NOIP练习赛题目5
  6. 自动提示_EXCEL2013版突然打不开,自动修复提示1907错误
  7. Good Numbers(HDU5447+唯一分解)
  8. 现在最流行哪款有linux系统,哪一个Linux发行版最流行?
  9. 删除MSSQL数据库text字段中恶意脚本Script=***.js/script的方法
  10. JS === 实现通过点击td 跳转相应的图片
  11. 磁盘阵列(RAID)-很详细的介绍
  12. android mmkv使用_[Android]高性能MMKV数据交互分析-MMKV初始化
  13. dreamweaver html模版,dreamweaver利用预设模板来快速的架构一个简易网站
  14. Excel宏的录制和解密
  15. 虚拟机centos7的网络配置
  16. 修改linux系统的时间EDT为CST
  17. 绘画新手怎么把人物骨骼画好
  18. ai人工智能_对人工智能的追求
  19. [RK3288][Android5.1] 移植笔记 --- gsl3673 触控驱动
  20. BootStrap2学习日记20---定制缩略图

热门文章

  1. 单片机红外通信c语言,用51单片机实现红外通讯源码
  2. 百度地图搜索框在弹出层中无法显示问题
  3. 广工计算机学院绩点,广工平均学分绩点计算器的教程
  4. 微信小程序使用身份证识别
  5. 是不是在为 API 烦恼 ?好用免费的api接口大全呼之欲出
  6. Barrier (屏障; 障碍; 栅栏; 分界线)
  7. 借贷宝java_程序猿去哪儿?——北京JAVA专场
  8. iOS Mac小白必备的基本操作
  9. uber幽灵车_Uber&Careem合并:新兴的出租车服务将在中东和北非地区产生影响?...
  10. 【虹科案例】极高的精度水平——虹科数字化仪在大型强子对撞机机器保护系统中的应用