1 | 等差素数列
题目
2,3,5,7,11,13,…是素数序列。
类似:7,37,67,97,127,157 这样完全由素数组成的等差数列,叫等差素数数列。
上边的数列公差为30,长度为6。
2004年,格林与华人陶哲轩合作证明了:存在任意长度的素数等差数列。
这是数论领域一项惊人的成果!
有这一理论为基础,请你借助手中的计算机,满怀信心地搜索:
长度为10的等差素数列,其公差最小值是多少?
思路
抓住题目的两个关键点:1. 等差数列
,2. 素数
- 等差数列中每前后项的差都是相等的,即存在一个确定的公差
- 素数列中每一项都是素数
思路大概如下
- 选定一个数作为后面构造等差数列的公差(因为题目要求最小公差,所以公差可以从1开始遍历选择)
- 选定一个素数作为等差素数列的首项(等差素数列的首项应是最小或最大的,为了方便计算,可选择2为每一次尝试的首项)
- 由这个
首项 + 公差
构造出后面每一项的值 - 判断这个等差数列中是否每一项都是素数,是则返回这个等差素数列,反之继续
逻辑代码
int ArithmeticPrimeSequence(int i_uNum)
{
if (0 > i_uNum)
{
return 0;
}
vector<int> res;
int ComRatioMin = 1;
for (int i = 1; i < 1000; i++)
{
for (int j = 2; j < 10000; j++)
{
if (IsPrimeNum(j))
{
int tmp = j;
res.push_back(tmp);
for (int k = 1; k < i_uNum; k++)
{
tmp += i;
if (IsPrimeNum(tmp))
{
res.push_back(tmp);
}
else
{
res.clear();
break;
}
}
}
if (i_uNum == res.size())
{
ComRatioMin = i;
break;
}
}
if (i_uNum == res.size())
{
ComRatioMin = i;
break;
}
}
cout << "The minimum common ratio = [" << ComRatioMin << "]." << endl;
cout << "Result sequence: [";
for (int i = 0; i < res.size(); i++)
{
if (i == res.size() - 1)
{
cout << res[i] << "]." << endl;
}
else
{
cout << res[i] << " ";
}
}
return 1;
}
2 | 错误票据
题目
某涉密单位下发了某种票据,并要在年终全部收回。
每张票据有唯一的ID号。全年所有票据的ID号是连续的,但ID的开始数码是随机选定的。
因为工作人员疏忽,在录入ID号的时候发生了一处错误,造成了某个ID断号,另外一个ID重号。
你的任务是通过编程,找出断号的ID和重号的ID。
假设断号不可能发生在最大和最小号。
输入格式
要求程序首先输入一个整数N(N<100)表示后面数据行数。
接着读入N行数据。
每行数据长度不等,是用空格分开的若干个(不大于100个)正整数(不大于100000),以-1结束每行输入
每个整数代表一个ID号。
输出格式
要求程序输出1行,含两个整数m n,用空格分隔。
其中,m表示断号ID,n表示重号ID
样例
输入1
2
5 6 8 11 9
10 12 9
输出1
7 9
输入2
6
164 178 108 109 180 155 141 159 104 182 179 118 137 184 115 124 125 129 168 196 -1
172 189 127 107 112 192 103 131 133 169 158 -1
128 102 110 148 139 157 140 195 197 -1
185 152 135 106 123 173 122 136 174 191 145 116 151 143 175 120 161 134 162 190 -1
149 138 142 146 199 126 165 156 153 193 144 166 170 121 171 132 101 194 187 188 -1
113 130 176 154 177 120 117 150 114 183 186 181 100 163 160 167 147 198 111 119 -1
输出2
105 120
思路
- 因为所有的ID都是连续的,所以先将所有ID进行排序
- 断号ID的特征就是它的上一个ID与下一个ID差值不为1
- 重复ID的特征就是前后两个ID值一样
逻辑代码
void WrongTicket(int i_uNum)
{
vector<int> res;
int BrokenNum = 0;
int RepeatNum = 0;
for (int i = 0; i < i_uNum; i++)
{
int tmp = 0;
while (tmp != -1)
{
cin >> tmp;
res.push_back(tmp);
}
}
ort(res, true);
for (int i = 0; i < res.size() - 1; i++)
{
if (res[i] == res[i + 1])
{
RepeatNum = res[i];
}
if ((res[i] != (res[i + 1] - 1)) && (res[i] != res[i + 1]))
{
BrokenNum = res[i] + 1;
}
}
cout << "Broken number = [" << BrokenNum << "]" << endl;
cout << "Repeat number = [" << RepeatNum << "]" << endl;
}
3 | 神奇算式
题目
由4个不同的数字,组成的一个乘法算式,它们的乘积仍然由这4个数字组成。
比如:
210 x 6 = 1260
8 x 473 = 3784
27 x 81 = 2187
都符合要求。
如果满足乘法交换律的算式算作同一种情况,那么,包含上边已列出的3种情况,一共有多少种满足要求的算式
思路
神奇算式有两个关键点
- 两个乘数的位数和 = 4
- 乘积的位数仍为 4,且这四位数是两个乘数的位数
大概思路如下:
-
确定两个乘数的范围
-
记录下两个数的数值,以及各个位的数字,并判断这两个数是否符合要求
怎么记录各个位的数值?
定义一个10长度的数组,如果0~9刚好对应各个数字,如果出现对应数字,数组对应位加一即可 -
判断乘积是否符合要求
怎么对比乘积的四位数与乘数的四位数是否相同?
通过使用上面定义的数组,获取该乘积的每一位,对应位的数值减一,如果最后数组中出现-1值,或非全0值,则证明四位数不相同 -
排除干扰项
什么是干扰项?
如下列两个式子看起来都是神奇算式
30 * 51 = 1530
51 * 30 = 1530
这两个式子实际上属于同一个神奇算式,只是两个乘数位置对调了,如果不排除这个类型的式子的话,就会导致得到的结果存在多个重复项
怎么排除?
只需要判断当前的两个乘数是否存在于前面存下来的数组中即可
逻辑代码
int MagicFormula()
{
vector<int> num(10, 0);
vector<pair<int, int>> Mult;
int res = 0;
int min1 = 0;
int max1 = 0;
int min2 = 0;
int max2 = 0;
for (int i = 1; i <= 2; i++)
{
if (1 == i)
{
min1 = 1;
max1 = 9;
min2 = 123;
max2 = 987;
}
else
{
min1 = 12;
max1 = 98;
min2 = 12;
max2 = 98;
}
for (int j = min1; j <= max1; j++)
{
for (int k = min2; k <= max2; k++)
{
int sum = 0;
if (IsValidMultiplier(j, k, num))
{
sum = j * k;
int tmp = sum;
while (tmp)
{
num[tmp % 10]--;
tmp /= 10;
}
int cnt = count(num.begin(), num.end(), 0);
if (10 == cnt)
{
int cnt1 = count(Mult.begin(), Mult.end(), pair<int, int>(j, k));
int cnt2 = count(Mult.begin(), Mult.end(), pair<int, int>(k, j));
if (0 == cnt1 && 0 == cnt2)
{
Mult.emplace_back(j, k);
cout << j << " * " << k << " = " << sum << endl;
res++;
}
}
}
fill(num.begin(), num.end(), 0);
}
}
}
return res;
}