简单枚举?你管这叫简单???
这刘汝佳说的。。。
已经好久没碰数学的我感觉被针对了。。。
PS:题目确实不难,但是都需要动动小脑袋去优化。
除法(Division, UVa 725)
输入正整数n,按从小到大的顺序输出所有形如abcde/fghij = n
的表达式,其中a~j恰好为数字0~9的一个排列(可以有前导0),2≤n≤79。
测试示例
样例输入:
62
样例输出:
79546 / 01283 = 62
94736 / 01528 = 62
思路
首先题目中要求a~j
是数字0~9
的一个排列,这说明这些数字不能重复出现,0~9
恰好都出现且仅出现一次。这是我出错的地方。
还有,不用枚举所有可能的分子和分母,判断是否符合规则并且相除等于n,这样所需要的时间复杂度是\(O(n^2)\),要做大概\(10^8\)次循环。简言之就是利用我们已知的n来减少循环次数。
我们可以把等式变形,\(abcde = n \times fghij\),然后我们只需要枚举\(fghij\)就能得到\(abcde\)然后判断两个数是否符合规则即可。复杂度降到了线性时间,需要做不到\(10^4\)循环。
代码
#include "iostream"
#include "cstdio"
#include "cstring"
using namespace std;
// str是 "abcde / fghij"
char str[14] = { '0','0','0','0','0',' ','/',' ','0','0','0','0','0',0 };
// 判断两个数是否满足要求
bool is_vaild() {
int varr[] = { 1,1,1,1,1,1,1,1,1,1 };
for (int i = 0; i < 13; i++) {
int cur = str[i]- '0';
if (cur >= 0 && cur <= 9) {
if (!varr[cur])return false;
varr[cur] = 0;
}
}
return true;
}
// 将整数ab转换成字符串
void to_str(int a, int b) {
int pos = 4;
str[0] =str[8]= '0'; // 前导0
while (pos >= 0 && a != 0) {
str[pos--] = a % 10 + '0'; a /= 10;
}
pos = 12;
while (pos >= 8 && b != 0) {
str[pos--] = b % 10 + '0'; b /= 10;
}
}
int main() {
int n;
bool first = true;
while (scanf("%d", &n) != EOF && n!=0) {
// 打印恶心的空行
if (!first)printf("\n");
else first = false;
int cnt = 0;
for (int b = 1000; b < 99999; b++) {
int a = b * n;
// a随b单调递增,所以当a超过了五位就可以退出了
if (a > 99999)break;
to_str(a, b);
if (!is_vaild()) continue;
cnt++;
printf("%s = %d\n", str, n);
}
if (cnt == 0) {
printf("There are no solutions for %d.\n", n);
}
}
return 0;
}
最大乘积(Maximum Product, UVa 11059)
输入n个元素组成的序列S,你需要找出一个乘积最大的连续子序列。如果这个最大的乘积不是正数,应输出0(表示无解)。\(1≤n≤18,-10≤Si≤10\)。
输入输出示例
样例输入:
3
2 4-3
5
2 5 -1 2 -1
样例输出:
Case #1: The maximum product is 8.
Case #2: The maximum product is 20.
思路
这题没啥可说的,就是编码的时候小心点就好了,我他妈WA了七八次。
连续子序列有一个起始位置,一个结束位置,遍历这两个位置即可。
需要注意的是,序列最长18个,单个元素绝对值最大\(10\),得用long long。
代码
#include "iostream"
#include "cstdio"
using namespace std;
int main() {
int n;
int arr[18];
long long max;
int times = 0;
while (scanf("%d", &n) != EOF && n!=0) {
for (int i = 0; i < n; i++) scanf("%d", &arr[i]);
//if (times)printf("\n");
times++;
max = 0;
for (int l = 0; l < n; l++) {
long long mul = 1;
for (int r = l; r < n; r++) {
mul = mul * arr[r];
max = mul > max ? mul : max;
}
}
printf("Case #%d: The maximum product is %lld.\n\n",times,max);
}
return 0;
}
分数拆分(Fractions Again?!, UVa 10976)
输入正整数k,找到所有的正整数x≥y,使得\(\frac{1}{k}=\frac{1}{x} + \frac{1}{y}\)
输入输出示例
样例输入:
2
12
样例输出:
2
1/2 = 1/6 + 1/3
1/2 = 1/4 + 1/4
8
1/12 = 1/156 + 1/13
1/12 = 1/84 + 1/14
1/12 = 1/60 + 1/15
1/12 = 1/48 + 1/16
1/12 = 1/36 + 1/18
1/12 = 1/30 + 1/20
1/12 = 1/28 + 1/21
1/12 = 1/24 + 1/24
思路
根据第一题的思路,我们知道肯定不能遍历所有的xy,要把等式做些调整,利用上我们已经知道的k来减少循环次数。
最后把等式化简成这样的形式:\(x=\frac{ky}{y-k}\)
这样做有几点好处:
- 同第一题,利用k减少循环次数
- 减少强转和小数运算带来的精度损失(如果很多个分式,会出现精度损失,造成漏掉正确答案的后果)
- 这样能缩小y的下界,题目中只说xy是正整数,那么y就得从1开始循环,而这个分式说明了y必须大于k,所以y可以从k+1处开始循环。
还有就是y的上界,不能一直循环下去,要找到一个上界来停止y的循环。
所以y循环到\(2k\)即可。
代码
#include "iostream"
#include "cstdio"
#include "vector"
using namespace std;
int main() {
int k;
while (scanf("%d", &k) != EOF && k!=0) {
vector<pair<int, int> > ans;
for (int y = k+1; y <= 2*k; y++) {
double x = (k * y) / (double)(y - k);
int int_x = (int)x;
if (int_x < x)continue;
ans.push_back(make_pair(int_x, y));
}
printf("%d\n", ans.size());
for (int i = 0; i < ans.size(); i++) {
pair<int, int> xy = ans[i];
printf("1/%d = 1/%d + 1/%d\n", k, xy.first, xy.second);
}
}
return 0;
}