离散数学实验课中有趣的题目

这学期上离散数学实验课,遇到了两个有意思的题目

        第一个是输入表达式例如p&q,求出这个表达式的真值表,进而求出它的主合取范式和主析取范式。

        其实后面的很无聊,有意思的是根据表达是生成真值表,这就类似于给你一个数学表达式(a+b)/(c*d),然后根据a b c d 的不同取值求出表达式的结果,这里解析这个表达式是一个重点,还要考虑括号的优先级问题,如果你想到把中缀表达式换成后缀表达式,那么好吧,你已经大概知道这个程序的主要结构了~

#include <iostream>
#include <cmath>
#include<stack>
#include<deque>
#include<string>
#include<map>
using namespace std;
const int M = 10;		//真值表最大列数
const int N = 520;		//真值表最大行数

class Expression {
private:
	string coll1;  //中缀表达式
	stack<char> coll2;  //操作符
	deque<char> coll3; //后缀表达式
	stack<int> coll4;  //辅助计算后缀表达式
	//获得条件表达式的值
	int getValueSingle(int a, int b){
		if (a == 1 && b == 0)
			return 0;
		else
			return 1;
	}
	//获得双条件表达式的值
	int getValueDouble(int a, int b){
		if ((a == 1 && b == 1) || (a == 0 && b == 0))
			return 1;
		else
			return 0;
	}
	//获得符号的优先性
	int getPri(char c) {  
		switch(c) {
			case '!':
				return 4;  
				break;
			case '&':
				return 3;  
				break;
			case '|':
				return 2;
				break;
			case '-':
				return 1;
				break;
			case '=':
				return 0;
				break;
			case '(':
			case ')':
				return -1; 
				break;
		}
	}
	//处理符号
	void check(char c) {
		if(coll2.empty()) {
			coll2.push(c);
			return;
		}
		if(c == '(' || c == ')') {
			if(c=='(') 
				coll2.push(c);
			else {
				while(coll2.top()!='(') {  //弹出所有元素直到遇到左括号
					char ch = coll2.top();
					coll3.push_back(ch);
					coll2.pop();
				}
				coll2.pop();
			}
		 } 
		 else { 
			char sym = coll2.top();  
			if(getPri(c)<=getPri(sym))  { 
				coll2.pop();   
				coll3.push_back(sym);  
				check(c);  
			}
			else {
				coll2.push(c); 
			}
		 }
	}
	//从coll中取出元素,分配元素到coll2和coll3中
	void allocate() {  
		for (int i=0; i<coll1.size(); i++) {
			char c = coll1[i];
			if('A'<c && 'Z'>c) {
				coll3.push_back(c);
			}
			else 
				 check(c); 
		}	
		while(!coll2.empty()) {  //如果输入结束,将coll2的元素全部弹出,加入后缀表达式中
			char c = coll2.top();
			coll3.push_back(c);
			coll2.pop();
		}
	}
public:
	Expression(string str): coll1(str){
		allocate();  //从coll中取出元素,分配元素到coll2和coll3中
	}
	//计算后缀表达式
	int calculate(char bianyuan[], int array[], int n) {
		deque<char>coll3 = this->coll3;
		stack<int>coll4;//辅助计算结果
		map<char, int> myMap;
		for (int i=0; i<n; i++){
			myMap[bianyuan[i]] = array[i];
		}
		while(!coll3.empty()) {
			char c = coll3.front();
			coll3.pop_front();
			if('A'<c && c<'Z') { 
				int op = myMap[c];    
				coll4.push(op);     
			}
			else if (c == '!'){
				int op = coll4.top();
				coll4.pop();
				coll4.push(!op);
			}
			else {  //如果是操作符,从栈中弹出元素进行计算
				int op1 = coll4.top();
				coll4.pop();
				int op2 = coll4.top();
				coll4.pop();
				switch(c) {
					case '&':
						coll4.push(op2&op1);
						break;
					case '|':
						coll4.push(op2|op1);
						break;
					case '-':
						coll4.push(getValueSingle(op2, op1)); 
						break;
					case '=':
						coll4.push(getValueDouble(op2, op1));
						break;
				}
			}
		}
		return coll4.top();
	}
};
//二进制转化成为十进制
int changeToDec(int a[], int n){
	int sum = 0;
	for (int i=0; i<n; i++){
		sum += (int)pow(2, n-i-1) * a[i];
	}
	return sum;
}

int main(){
	string str;		//表达式
	int num;		//变元个数
	int small[M] = {0};		//存储小项
	int big[M] = {0};		//存储大项
    char bianyuan[M-1];		//变元数组
	int elements[N][M] = {0};		//真值表矩阵
	cout << "请输入变元数量(小于等于9)" << endl;
	cin >> num;
	cout << "请依次输入各个变元名称" << endl;
	char temp;
	//输入变元并判断输入是否合法
	for (int i=0; i<num; i++){
		while (true){
			cin >> temp;
			if (temp<'A' || temp>'Z')
				cout << "请输入大写字母" << endl;
			else{
				bool flag = true;
				for (int k=0; k<M-1; k++){
					if (temp == bianyuan[k]){
						cout << "不能与已输入字符相同" << endl;
						flag = false;
						break;
					}
				}
				if (!flag)
					continue;
				bianyuan[i] = temp;
				break;
			}
		}
	}
	//生成真值表
	int rows = (int)pow(2, num);		//行数
	for (int j=0; j<num; j++){
		int flag = 0;
		int k = (int)pow(2, num-j-1);
		int count = 0;
		for (int l=0; l<rows; l++){
			if (!flag){
				elements[l][j] = 0;
				count++;
			}
			else{
				elements[l][j] = 1;
				count++;
			}
			if (!(count % k))
				flag = !flag;
		}
	}
	//输入表达式并得到结果
	cout << "请输入表达式";
	cin >> str;
	Expression e(str);
	int rcount = 0;
	int lcount = 0;
	for (int m=0; m<rows; m++){
		elements[m][num] = e.calculate(bianyuan, elements[m], num);
		
		if (elements[m][num] == 1){
			small[rcount] = changeToDec(elements[m], num);
			rcount++;
		} else {
			big[lcount] = changeToDec(elements[m], num);
			lcount++;
		}
	}
	//输出真值表
	for (int b=0; b<num; b++){
		cout << bianyuan[b] << " ";
	}
	cout << endl;
	for (int a=0; a<rows; a++){
		for (int b=0; b<=num; b++){
			if (elements[a][b] == 1)
				cout << 'T' << " ";
			else
				cout << 'F' << " ";
		}
		cout << endl;
	}
	//输出结果
	cout << "主析取范式如下" << endl;
	for (int k=0; k<rcount; k++){
		cout << "m" << small[k];
		if (k!=rcount-1)
			cout << "||";
	}
	cout << endl;
	cout << "主合取范式如下" << endl;
	for (int l=0; l<lcount; l++){
		cout << "M" << big[l];
		if (l!=lcount-1)
			cout << " && ";
	}
	cout << endl;
	return 0;
}


	
        程序里都有注释,相信几年以后我再回来看这篇文章应该还是能看懂。

        第二个程序是关于欧拉回路的寻找,这里要用到深度优先的搜索方法。

int P[100][100];		//邻接矩阵
int closeP[100][100];	//邻接矩阵的闭包
int visited[100][100];	//深度优先搜索中已经搜索过的 边
int print[10000];		//路或者回路
int numOfBian = 0;		//边的数量

#include <iostream>
#include <ctime>
#include <cstdlib>
using namespace std;
void createP(int n){	//根据节点数量随机创建邻接矩阵
	for (int i=0; i<n; i++){
		for (int j=0; j<n; j++){
			if (i == j){
				P[i][j] = 0;
			} else {
				int temp = rand()%2;
				P[i][j] = temp;
				P[j][i] = temp;
			}
		}
	}
}
void printP(int n){		//输出邻接矩阵
	for (int i=0; i<n; i++){
		for (int j=0; j<n; j++){
			cout << P[i][j] << " ";
		}
		cout << endl;
	}
}
bool isLianTong(int n){		//生成邻接矩阵的闭包并判断是否连通
	for (int i=0; i<n; i++){
		for (int j=0; j<n; j++){
			closeP[i][j] = P[i][j];
		}
	}
	for (i=0; i<n; i++){
		for (int j=0; j<n; j++){
			if (closeP[j][i] != 0){
				for (int k=0; k<n; k++){
					closeP[j][k] += closeP[i][k];
				}
			}
		}
	}
	for (i=0; i<n; i++){
		for (int j=0; j<n; j++){
			if (closeP[i][j] == 0)
				return false;
		}
	}
	return true;
}

			                                                               
int judge(int n){		//根据奇度数节点和偶度数节点的个数判断图是否为欧拉图(半欧拉图)
	int countJi = 0;
	int countOu = 0;
	for (int i=0; i<n; i++){
		int count = 0;
		for (int j=0;j<n; j++){
			if (P[i][j] == 1)
				count++;
		}
		if (count % 2 == 0)
			countOu++;
		else 
			countJi++;
	}
	//cout << countJi;
	//cout << countOu;
	if (countJi == 0 && countOu == n){
		cout << "是欧拉图" << endl;
		return 1;
	}
	else if (countJi == 2){
		cout << "是半欧拉图但不是欧拉图" << endl;
		return 2;
	}
	else{
		cout << "不是欧拉半图" << endl;
		return 3;
	}
}

bool visit(int n, int first, int shengdu){//深度优先搜索欧拉回路
	//cout << first << "," << shengdu << endl;
	if (shengdu == numOfBian && first == 0){
		print[shengdu] = first;
		return true;
	}
	for (int i=0; i<n; i++){
		if ((P[i][first] == 1||P[first][i] == 1) && visited[first][i] == 0 && visited[i][first] == 0){
			visited[first][i] = 1;
			visited[i][first] = 1;
			if (visit(n, i,shengdu+1)){
				print[shengdu] = first;
				return true;
			} else {
				visited[first][i] = 0;
				visited[i][first] = 0;
			}
		}
	}
	return false;
}
bool visitHalf(int n, int first, int shengdu){//深度优先搜索欧拉路(非欧拉回路的欧拉路)
	//cout << first << "," << shengdu << endl;
	if (shengdu == numOfBian){
		print[shengdu] = first;
		return true;
	}
	for (int i=0; i<n; i++){
		if ((P[i][first] == 1||P[first][i] == 1) && visited[first][i] == 0 && visited[i][first] == 0){
			visited[first][i] = 1;
			visited[i][first] = 1;
			if (visitHalf(n, i,shengdu+1)){
				print[shengdu] = first;
				return true;
			} else {
				visited[first][i] = 0;
				visited[i][first] = 0;
			}
		}
	}
	return false;
}
void findEuler(int n){//搜索欧拉回路
	numOfBian = 0;
	for (int i=0; i<n; i++){
		for (int j=0; j<n; j++){
			if (P[i][j] == 1){
				numOfBian++;
			}
		}
	}
	numOfBian /= 2;
	//cout << numOfBian;
	memset(print, 0, sizeof(print));
	memset(visited, 0, sizeof(visited));
	if (visit(n, 0, 0)){
		cout << "回路路径为" << endl;
		for (i=0; i<=numOfBian; i++){
			if (i == 0)
				cout << print[i];
			else
				cout << "->" << print[i];
		}
		cout << endl;
	}
}
			
void findHalfEuler(int n){//搜索欧拉路(非欧拉回路的欧拉路)
	numOfBian = 0;
	for (int i=0; i<n; i++){
		for (int j=0; j<n; j++){
			if (P[i][j] == 1){
				numOfBian++;
			}
		}
	}
	numOfBian /= 2;
	//cout << numOfBian;
	memset(print, 0, sizeof(print));
	memset(visited, 0, sizeof(visited));
	if (visitHalf(n, 0, 0)){
		cout << "路路径为" << endl;
		int c = 0;
		for (i=0; i<=numOfBian; i++){
			for (i=0; i<=numOfBian; i++){
				if (i == 0)
					cout << print[i];
				else
					cout << "->" << print[i];
			}
		cout << endl;
		}
	}
}



int main(){
	int n;
	srand(time(NULL));
	cout << "请输入节点个数";
	cin >> n;
	createP(n);
	printP(n);
	int res = 0;
	if (isLianTong(n)){
		res = judge(n);
	} else 
		cout << "不连通" << endl;
	if (res == 1)
		findEuler(n);
	if (res == 2)
		findHalfEuler(n);
	
	return 0;
}

        其实第二个程序我不确定正确,我只用过有限的几个测试用例,如果要测试的话,得改一下生成邻接矩阵的函数。

        突然发现,数据结构算法这些东西好有趣~




版权声明:本文为u012427462原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。