【算法练习】Acwing第44周周赛

二、最短路径(中等)

在这里插入图片描述
在这里插入图片描述
题目的第一个条件很好保证,关键在于第二个条件,如何保证从起点到终点,是最短的安全路径(之一)?

考虑下面这种情况:
在这里插入图片描述
虚线处代表距离为1,上面这种走的形式肯定不是最优的,因为明明可以走虚线,它非得绕一圈再走过去,这种情况怎么判断?很简单,对于当前点,遍历其四个方向,看周围已经访问过的点的个数是否 > 1,1是因为来的路径肯定会被访问,> 1例如等于2时,就说明当前点可以由其它更近点转移而来,也就代表给出的字符串不是最短路径(之一),输出NO。

import java.util.*;
import java.io.*;

public class Main {
    static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
    static BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(System.out));
    public static void main(String[] args) throws IOException {
        String input = reader.readLine();
        int x = 200, y = 200;
        boolean[][] vis = new boolean[400][400];
        vis[x][y] = true;
        boolean flag = false;
        int[] xx = new int[] {-1,1,0,0};
        int[] yy = new int[] {0,0,-1,1};
        for (int i = 0; i < input.length(); i++) {
            if (input.charAt(i) == 'U') {
                x -= 1;
            } else if (input.charAt(i) == 'D') {
                x += 1;
            } else if (input.charAt(i) == 'L') {
                y -= 1;
            } else {
                y += 1;
            }
            if (vis[x][y]) {
                flag = true;
                writer.write("NO");
                break;
            }
            // 标记已访问
            vis[x][y] = true;
            int cnt = 0;
            for (int j = 0; j < 4; j++) {
                int tx = x + xx[j];
                int ty = y + yy[j];
                if (vis[tx][ty]) cnt++;
            }
            if (cnt > 1) {
                flag = true;
                writer.write("NO");
                break;
            }
        }
        if (flag == false) writer.write("YES");
        writer.flush();
    }
}

三、合适数对(困难)

在这里插入图片描述
经典数论压轴,看着人就麻。

算术基本定理:任何一个大于1的自然数N,如果N不是质数,那么N可以唯一分解成有限个质数的乘积(如果N是质数,那就只能乘上自己N)。

如果一个数N,是某个数的K次幂,那么它的不同质因数的次数是k的倍数,例如:16 = 2 * 2 * 2 * 2 = 2^4,假定k=2,4是2的倍数,所以16是某个数的K次幂。也就是说我们只用关心当前数的质因数的次数模K的结果。

如果是两个数的乘积,对于公共的质因数,该质因数的次方应该是(a + b),不论公共或是独有的质因数的次数,都应该是K的倍数,例如:4 * 9 = 2 * 2 * 3 * 3 = 2^2 * 3^2,不同的质因数都是k=2的倍数,所以4 * 9可以表示为某个数的平方。

对于每一个数,最多为1e5 = 100000,十万,最多有6个不同的质因数:
2 * 3 * 5 * 7 * 11 * 13 * 17 = 510510
在这里插入图片描述

对一个数分解质因子,普通做法:O(根号n),可以用线性筛,线性筛可以知道每个数的最小质因子,而每个数的质因子个数只有logn个,所以只需要O(logn)就可以对某个数进行质因数分解。对于当前数x,求得其最小质因子y,然后再去求x/y的最小质因子即可。

假如一个数N = p1^k1 * p2^k2 * p3^k3,k1、k2都是k的倍数,k3不是,那么,就需要找到另外一个数去把k3凑成k的倍数,那么另外一个数就应该 = power(p3, k - k3),当前数N自己也可以为后面的数贡献p3^k3这个数,方便后面的数使用,同样进行记录。在计算power时,由于k = 200,当k3 = 1时,就算是2的199次方,也是很大的数,所以,如果计算power过程中的值大于数组中可能的最大值,那就直接返回0,代表当前数无意义(不可能找到配对的另一个数)。

import java.util.*;
import java.io.*;

public class Main {
    static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
    static BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(System.out));
    static long power(int a, int b) {
        long ans = 1;
        while (b-- > 0) {
            ans *= a;
            // 求得的结果大于数组中可能的最大值,无意义
            if (ans >= 100010) ans = 0;
        }
        return ans;
    }
    public static void main(String[] args) throws IOException {
        String[] input = reader.readLine().trim().split(" ");
        int n = Integer.parseInt(input[0]);
        int k = Integer.parseInt(input[1]);
        // 数组模拟哈希表
        int[] cnt = new int[100010];
        long ans = 0;  // 记录对数
        input = reader.readLine().trim().split(" ");
        int idx = 0;
        while (n-- > 0) {
            int cur = Integer.parseInt(input[idx++]);
            long y = 1, z = 1;  // y记录
            // 质因数分解,合数最后剩下1
            // 如果质因数的次数不是k的倍数,那就用z去补全缺失的次数
            for (int i = 2; i * i <= cur; i++) {
                if (cur % i == 0) {
                    int s = 0;
                    while (cur % i == 0) {
                        s++;
                        cur /= i;
                    }
                    // 查看当前质因数模k的余数
                    s %= k;
                    if (s != 0) {
                        // 次数不是k的倍数,那就需要另一半凑出k - s的值
                        y *= power(i, s);  // 只记录质因数的次数不是k的倍数的值
                        z *= power(i, k - s);
                    }
                }
            }
            // 质数的情况,例如:17
            if (cur > 1) {
                y *= cur;
                z *= power(cur, k - 1);
            }
            // 如果当前需要的数超过数组中最大数,显然是不可能的
            if (z >= 100010) z = 0;
            // z是当前数y需要的另一半补全的值
            ans += cnt[(int)z];
            // y是当前数中质因子不是k的倍数的乘积,从前往后遍历,方便后续数使用
            // eg: 24 = 2 * 2 * 2 * 3 = 2^3 * 3^1,如果k = 2,那么y = 2^3 * 3^1
            // z = 2 * 3 = 6,也就是说当前y要乘上6才能补成某个数的平方
            cnt[(int)y]++;
            // 1 1的情况也是满足的,1*1可以表示成1的任意次方
        }
        writer.write(ans + "");
        writer.flush();
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

@u@

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值