计算第k个答案-----摩尔斯电码字典(动态规划 + 递归)

题目:

摩尔斯电码字典
在没有电话的时代,摩尔斯电码是无线电传输领域中的一种常用代码。电码以短信号(短点,o)和长信号(长点,-)的不同组合表示各种文字。例如:o—表示英文字母J,而—表示英文字母M。
假设有一本以n个长点和m(n、m<=100)个短点组成的、包含所有信号的字典。例如:n=m=2,就会包含如下信号。
–oo
-o-o
-oo-
o–o
o-o-
oo–
这些信号已按照字典顺序排列好了。-的ASKII码是45,而o的ASCII码是111。因此,按照字典顺序,-在前,o在后。给定n和m时,编写代码计算出此字典的第k(k<=1,000,000,000,000)个信号。例如:上述字典的第四个信号是o–o。

代码

#include<iostream>
#include<string>
#include<algorithm>
#include<graphics.h>
#include<stdlib.h>
#include<conio.h>
#include<Windows.h>
using namespace std;
const int maxn = 1e3 + 5;
double pre[maxn][maxn];
//预处理,计算出所有的组合总数
void pre_solve(int n, int m) {
	//字典序:长点在前,短点在后
	for (int i = 1; i <= n; i++)
		pre[i][0] = 1;
	for (int i = 1; i <= m; i++)
		pre[0][i] = 1;
	//dp,利用状态转移方程求出所有组合
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= m; j++)
			pre[i][j] = pre[i - 1][j] + pre[i][j - 1];
	}
}
/*
运用递归找出答案
首先,假设第一个是'-',则前半部分有number = pre[n - 1][m]种组合
比较k和number的大小,判断是在前半部分还是后半部分
判断是前半部分还是后半部分
*/
string find_answer(int n, int m, double k) {
	if (k > pre[n][m])
		return "Error !";
	if (n == 0)
		return string(m, 'o');
	if (m == 0)
		return string(n, '-');
	//前半部分递归
	if (k <= pre[n - 1][m])
		return '-' + find_answer(n - 1, m, k);
	//后半部分递归,前半部分没有,所以要在后半部分找第k-number
	return 'o' + find_answer(n, m - 1, (k - pre[n - 1][m]));
}
void solve(int n, int m) {
	double k;
	wchar_t s[10];
	InputBox(s, 10, _T("查看第k和信号,请输入k"));
	HWND hwnd;
	hwnd = GetHWnd();
	k = _ttoi(s);
	string ans;
	ans = find_answer(n, m, k);
	int t = 0;
	char s1[maxn * 2 + 5];
	for (int i = 0; i < ans.length(); i++, t++)
		s1[t] = ans[i];
	s1[t] = '\0';
	cleardevice();
	wchar_t temp[maxn * 2 + 5];
	_stprintf_s(temp, maxn * 2 + 5, _T("%S"), s1);
	int text_size = min(1300 / (m + n) + 5, 35);
	RECT r = { 0, 0, 1299, 599 };
	settextstyle(text_size, 0, _T("黑体"));
	drawtext(temp, &r, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
	getch();
}
void input(int& n, int& m) {
	wchar_t s1[10];
	InputBox(s1, 10, _T("请输入长点n"));
	HWND hwnd;
	hwnd = GetHWnd();
	n = _ttoi(s1);
	wchar_t s2[10];
	InputBox(s2, 10, _T("请输入短点m"));
	m = _ttoi(s2);
}
void graph_case(int& n, int& m) {
	cleardevice();
	settextstyle(30, 0, _T("楷体"));
	outtextxy(450, 170, _T("1.输入长点和短点的数量"));
	outtextxy(450, 270, _T("2.计算第k个信号"));
	outtextxy(450, 370, _T("3.退出系统"));
	getch();
	wchar_t s[10];
	InputBox(s, 10, _T("请输入要进行的操作"));
	HWND hwnd;
	hwnd = GetHWnd();
	int ch = _ttoi(s);
	if (ch == 1) {
		input(n, m);
		pre_solve(n, m); //进行预处理
	}
	if (ch == 2) {
		if (n == 0 && m == 0) {
			cleardevice();
			RECT r = { 0, 0, 1299, 599 };
			drawtext(_T("错误!请先输入长点和短点的数量"), &r, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
			getch();
		}
		else
			solve(n, m);
	}
	if (ch == 3)
		exit(0);
}
void graph() {
	initgraph(1300, 600);
	setbkcolor(WHITE);
	cleardevice();
	settextcolor(BLACK);
	settextstyle(60, 0, _T("楷体"));
	outtextxy(450, 220, _T("摩尔斯电码字典"));
	settextstyle(30, 0, _T("楷体"));
	outtextxy(550, 320, _T("编写:***"));
	Sleep(1500);
	int n = 0, m = 0;
	while (1)
		graph_case(n, m);
}
int main()
{
	graph();
	return 0;
}

运行效果

请添加图片描述请添加图片描述请添加图片描述
在这里插入图片描述

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
数据结构关于二叉树的建立遍历以及应用二叉树进行编解码 实验要求 必做部分 1. 小明会按照前序的方式输入一棵二叉树。例如,输入$ACG##H##D##BE#I##F##的话,代表了下面这棵树: 2. 请分别按照前序、中序、后序输出这棵树。 选做部分 背景 在影视剧中,我们经常会看到二战期间情报人员使用电报哒哒哒地发送信息,发送电报所使用的编码叫做摩尔电码(或者叫做摩密码)。甚至在现代,SOS仍然是国际通用的求救信号之一,其“三短、三长、三短”同样是摩密码的编码方式。 摩密码使用若干个“点”和“划”来表示一个字母,字母和字母之间使用短暂的停顿来表示。例如,一种常见的编码方式为: 字母 摩密码 字母 摩密码 A .- E . B -... F ..-. C -.-. G --. D -.. H .... 实际上,一个摩密码本可以使用一棵二叉树来存储: 上图表示,从根节点start开始,遇到一个点(Dot)就访问它的左子树节点,遇到一个划(Dash)就访问它的右子树节点。例如,三个点...代表了S,三个划---代表了O。所以SOS的摩密码是... --- ...(中间用空格隔开,表示短暂的停顿)。再比如,爱疯手机有一种来电铃声的节奏为“哇哇哇 哇-哇- 哇哇哇”,这其实表示的是…… 现在,小明想在课上偷偷跟你传纸条,但又不想被其他同学看到内容。因此他跟你约定,每次给你传纸条时都使用摩密码来编写。至于密码本,当然不能使用国际通用的,他会在课前告诉你密码本的内容。然而小明发现,每次写纸条、读纸条都不是很方便,所以他想让你做个程序来自动编码/解码你们的摩密码。 题目要求 首先,小明输入的那棵二叉树,代表了你们在这堂课上要使用的摩密码本。例如,输入$ACG##H##D##BE#I##F##的话,代表了下面这棵树: 第一个字母$是什么并不重要,因为它只是代表了根节点,而根节点在我们的摩电码中并不代表一个字符,仅仅代表“start”。 读入密码本后,请记得按照前序、中序、后序输出这棵树。 然后,小明会输入一个数,代表接下来输入的是明文还是摩电码。输入0表示接下来他会输入明文,输入1表示接下来输入的是摩电码,输入-1程序退出。 1. 如果输入的是0,代表接下来要输入的是明文。程序接受一个字符串,根据字符串中每一个字母输出对应的摩电码,用空格隔开。例如如果小明输入“BED”,则程序应该输出“-空格-.空格.-”。如果遇到密码本中没有的字符,则输出“输入有误”。 2. 如果输入的是1,则表示接下来要输入摩电码。小明首先会输入一个数字N,代表有几个电码的输入,例如输入4代表之后会输入4个电码(即这个单词有四个字母)。随后输入空格分割的电码,例如, .. . -- -. 程序需要根据摩电码解读出明文单词并输出,例如上面的电码表示“CAFE”。如果遇到密码本中没有的编码,则输出“输入有误”。 输入输出样例 必做部分: 请输入二叉树: $ACG##H##D##BE#I##F## 前序遍历:$ACGHDBEIF 中序遍历:GCHAD$EIBF 后序遍历:GHCDAIEFB$ 选做部分: 请选择(0为明文,1为电码,-1退出):0 请输入明文:BED 摩电码为:- -. .- 请选择(0为明文,1为电码,-1退出):1 请输入电码个数:4 请输入电码:.. . -- -. 明文为:CAFE 请选择(0为明文,1为电码,-1退出):-1 // 程序结束

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值