Loading

简单枚举?你管这叫简单???

这刘汝佳说的。。。

已经好久没碰数学的我感觉被针对了。。。

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}\)

这样做有几点好处:

  1. 同第一题,利用k减少循环次数
  2. 减少强转和小数运算带来的精度损失(如果很多个分式,会出现精度损失,造成漏掉正确答案的后果)
  3. 这样能缩小y的下界,题目中只说xy是正整数,那么y就得从1开始循环,而这个分式说明了y必须大于k,所以y可以从k+1处开始循环。

还有就是y的上界,不能一直循环下去,要找到一个上界来停止y的循环。

\[\because x \ge y,\frac{1}{k}=\frac{1}{x} + \frac{1}{y}且k,x,y \in N^+ \\ \therefore \frac{1}{x} \le \frac{1}{y}\\ \max(\frac{1}{k}) = \frac{1}{y}+\frac{1}{y}\\ \frac{1}{k} \le \frac{2}{y}\\ y\le 2 k \]

所以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;
}
posted @ 2020-11-24 10:20  yudoge  阅读(165)  评论(0编辑  收藏  举报