算法题实战:计算第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。
分析
这道题是求特定组合数,给n个长点和m个短点,按字典序排列好,求第K个信号组合。
数据范围:k<=1,000,000,000,000,使用long long 类型。
首先,用杨辉三角基本算法建立m+n行杨辉三角,通过杨辉三角计算出n个长点和m个短点所有组合的个数。杨辉三角的值就相当于是从 1到n+m的组合数的全排列,后面可以直接用。
通过vector动态开辟m+n*m+n二维数组,因为k<=1,000,000,000,000, 所以数组类型为long long类型。将组合数与杨辉三角联系起来,用f[i][j]=f[i−1][j]+f[i−1][j−1]f[i][j]=f[i−1][j]+f[i−1][j−1]基本原理双重循环计算出m+n行杨辉三角,保存在vector数组中。C(n+m,n-1)就相当于vector数组nums[n+m][n-1]。
取得k值后,比较k与对应组合数C(n+m,n)的大小确定在不在组成信号个数的范围内,超出范围退出,在范围内就调用com函数,来组合排列出所求信号。com函数通过二分法,动态递归写入string s字符串。
算法
分为三步:
1.假设第一个信号是短点-,判断k是否小于等于C(n+m-1,n-1),如果小于第一位就是短点,执行s=s+’-’;否则s=s+’。’,k=k-C(n+m-1,n-1);
2.如果第一位是’-’,递归com(n+m-1,n-1);如果第一位是’。'递归C(n+m-1,n)即重复一二步,直到n=0;
3.当n=0时结束递归,并判断m值,不等于0就循环写入s,等于0变结束函数。
代码
VS2017
#include<iostream>
#include<string>
#include<vector>
using namespace std;
string s;
vector<vector <_int64> > nums;
void basicYang(int numRows)//基本方法计算杨辉三角
{
int i, j;
for (i = 0; i < numRows; i++)
{
nums[i][0] = 1;
nums[i][i] = 1;
for (j = 1; j < i; j++)
nums[i][j] = nums[i - 1][j - 1] + nums[i - 1][j];
}
}
void Combin(int n, int m, _int64 k)
{
if (n > 0) {
if (k <= nums[n+m-1][n-1]) {
s = s + '-';
Combin(n - 1, m, k);
}
else {
s = s + 'o';
k = k - nums[n + m - 1][n - 1];
Combin(n, m - 1, k);
}
}
else {
while (m > 0) {
s = s + 'o';
m--;
}
}
}
int main()
{
int n, m;//n个长点,m个短点
_int64 k;
cin >> n >> m;
nums.resize(m + n + 1, vector<_int64>(m + n + 1,0));
basicYang(n + m +1);
cout << nums[n + m][n] << endl;
cin >> k;
if (k <= nums[n + m][n] && k > 0)
{
Combin(n, m, k);
cout << s << endl;
}
else {
cout << "K不在范围内" << endl;
}
nums.clear();
vector<vector <_int64> > num;
num.swap(nums);
system("pause");
return 0;
}