前段时间才做了离散实验的实验报告,为防止在本地遗失,所以上传到CSDN上一份,欢迎大家一起学习
熟悉掌握命题逻辑中的联结词、真值表、主范式等,进一步能用他们来解决实际问题。
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
#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
#define int long long
#define endl '\n'
using namespace std;
map 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;
}
}
} //当前位置不是字母时,判断该运算符前后是否为合法字母,若不是合法字母,则输出error
else
{
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
#define OP 0
#define NUM 1//宏定义确定该位置为操作符还是数字
using namespace std;
setoperator_set = { '(', ')', '=', '^', '!', '&', '|' };//操作集
maplevel; //优先级
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());
}//未到底端继续递归往下查找子树计算结果
}
};
stacknum_stk;
stackop_stk;
void addnode() {
TreeNode *R = num_stk.top();//右节点为栈顶元素
num_stk.pop();
TreeNode *L = new TreeNode(0);//定义新节点为0
char 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();//清空栈
vectorv;
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;
}
} //判断输入符号是否合法
setst;
mapmp;
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()) {
vectortemp;
for (int i = 1; i <= num0; i++) temp.push_back(0);
for (int i = 1; i <= num1; i++) temp.push_back(1);//按枚举数量分别放入相应数量0,1
do {//全排列枚举该数量下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';
}
}//将对应位置命题符号转化为01
for (auto it : temp) cout << it << ' ';//输出命题取值
cout << Expression(x) << endl;//输出真值
} while (next_permutation(temp.begin(), temp.end()));
num0--;
num1++;
}
}
C:
#include
#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;
}
集合论是一切数学的基础,也是计算机科学不可或缺的,在数据结构、数据库理论、开关理论、自动机理论和可计算理论等领域都有广泛的应用。集合的运算规则是集合论中的重要内容。通过该组实验,目的是让学生更加深刻地理解集合的概念和性质,并掌握集合的运算规则等。
(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+1i+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-1j的 j 的最大值,设为 i,即 i=max{j|aj-1j}
(2)求满足关系式 ai-1k的 k 的最大值,设为 j,即 j=max{k|ai-1k}
(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
#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 a, vector 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 a, vector b) //并集
{
cout << "并集:";
vector 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 a, vector b) //差集
{
cout << "差集:\n";
cout << "A-B:"; //先判断A-B
vector 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-A
d.clear(); //清空d数组
f = 0; //初始化f
for (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 a, b;
cout << "集合A:\n";
map 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
#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 a, b;
cout << "集合A:\n";
map 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
#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 a, b;
cout << "集合A:\n";
map 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
#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 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 a, b;
cout << "集合A:\n";
map 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全排序
}
图论是一个应用十分广泛而又极其有趣的数学分支,以离散对象的二元关系结构为研究
对象。图论在网络理论、信息论、控制论和计算机科学等领域都有着广泛的应用。通过该组
实验,目的是让学生更加深刻地理解图论中的一些基本概念,并熟悉图在计算机上的表示和
运算方法,且能够解决实际图论问题。
在简单图中求源点 a 到其它各点(b,c,…,j)的所有最短路径。
C或C++语言编程环境实现。
DEVC++ 5.4.0
MinGW GCC 4.7.2 32-bit
-std=c++17
A1(dijkstra朴素版):
#include
#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;//将起点设置距离为0
for (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;//若无路则输出-1
else cout << dist[i] << endl;//有路则输出路径
}
}
A2(dijkstra堆优化版本,复杂度更小):
#include
#define endl '\n'
#define int long long
using namespace std;
const int N = 1e6 + 100;
const int mod = 1e9 + 7;
typedef pair 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, greater> 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 << " ";
else
cout << dist[i] << " ";
}//这样输出第n行就是起点到第n个节点的最短路,若无路则输出-1
}