前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >挑战程序竞赛系列(38):4.1模运算的世界(1)

挑战程序竞赛系列(38):4.1模运算的世界(1)

作者头像
用户1147447
发布2019-05-26 09:41:06
2980
发布2019-05-26 09:41:06
举报
文章被收录于专栏:机器学习入门机器学习入门

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://cloud.tencent.com/developer/article/1434712

挑战程序竞赛系列(38):4.1模运算的世界(1)

详细代码可以fork下Github上leetcode项目,不定期更新。

练习题如下:

POJ 1150: The Last Non-zero Digit

总算刷到了登峰造极,看了一些关于模运算的相关知识,但还是云里雾里,索性就直接开题,该系列每题都开篇博文讲解吧,知识点比较多。

此题推荐博文:http://www.hankcs.com/program/algorithm/poj-1150-the-last-non-zero-digit.html

更详细的解法:http://duanple.blog.163.com/blog/static/7097176720081016113033592/

数论入门推荐书籍:《初等数论及其应用》.

思路:

进行质因数分解,分解2和5,因为只有2和5的乘积才能构成10。在一个序列中:

1 2 3 4 5 6 7 8 9 10

因数2的个数永远大于因数5的个数

所以说,我们只需要求出多出来的2的个数即可,这些将会影响非零位数的最终结果。

2的质因数个数如何求解:

1 2 3 4 5 6 7 8 9 10

可以分解为:
1 3 5 7 9 | 2 4 6 8 10
前半部分个数:0
后半部分个数:5 + f{1,2,3,4,5}

没错,除了个2之后,能够得到5个质因数
该问题就变成了子问题{1,2,3,4,5}的质因数个数

递归解决,复杂度O(logn)

代码如下:

    public int total2(int n){
        if (n == 0) return 0;
        return n / 2 + total2(n / 2);
    }

质因数5的个数如何?和2一个道理,所以有:

    public int total5(int n){
        if (n == 0) return 0;
        return n / 5 + total5(n / 5);
    }

所以综合下(递归版本):

    public int fact_prime(int n, int x){
        if (n == 0) return 0;
        return n / x + fact_prime(n / x, x);
    }

迭代版本:

    public int fact_prime_iter(int n, int x){
        int res = 0;
        while (n > 0){
            res += n / x;
            n = n / x;
        }
        return res;
    }

接着,把所有的2和5的质因数全部排除后,剩余了什么?

1 2 3 4 5 6 7 8 9 10
1 1 3 1 1 3 7 1 9 1

只剩下{1,3,7,9}了,那么为了能够计算非零位数,统计这些1,3,7,9的个数就完事了。

统计蛮取巧,分成奇数列和偶数列

1 3 5 7 9 | 2 4 6 8 10
前半部分的个数分别为:1:2 3:1 7:1 9:1
后半部分的个数分别为:子问题{1,2,3,4,5}

子问题可以不去考虑了,前半部分怎么统计呢?
观察发现它包含有5的奇数倍项,但这奇数倍均除以5得到1,3,5,7....
所以变成了前部部分的子问题了...

所以问题的关键就变成了求解1,3,7,9各自的个数了

其实都不需要求,直接看出来了
当n = 10,1:1, 3:1, 7:1, 9:1
当n = 13?
1:2 3:2 7:1 9:1
因为13中有11和13个位数分别为1和3

所以给定n
我们有 g(n, x) = n / 10 + (n % 10 >= x) + g(n / 5, x);

n/10: 每10个数都有一个
n%19: n的个位数可能产生一个

好了,代码就出来了:

    public int odd_end_with(int n, int x){
        if (n == 0) return 0;
        int res = n / 10;
        res += (n % 10 >= x ? 1 : 0);
        return res + odd_end_with(n / 5, x);
    }

    public int end_with(int n, int x){
        if (n == 0) return 0;
        return end_with(n / 2, x) + odd_end_with(n, x);
    }

最后如何计算N!(N−M)!\frac{N!}{(N - M)!}?

很简单,因为N-M完全包含N,所以直接把统计结果减掉即可,最终代码如下:

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.StringTokenizer;

public class Main{

    String INPUT = "./data/judge/201708/P1150.txt";

    public static void main(String[] args) throws IOException {
        new Main().run();
    }

    int[][] table = {
            {6, 2, 4, 8},
            {1, 3, 9, 7}, // 3  
            {1, 7, 9, 3}, // 7
            {1, 9, 1, 9}, // 9
    };

    void solve() {
        while (more()){
            int N = ni();
            int M = ni();

            // 分解质因数 2 和 5 并且分别统计 2 和 5 的个数
            int two    = fact_prime(N, 2) - fact_prime_iter(N - M, 2);
            int five   = fact_prime(N, 5) - fact_prime_iter(N - M, 5);
            int remain = two - five;

            // 统计 1 3 7 9 的个数
            int three  = end_with(N, 3) - end_with(N - M, 3);
            int seven  = end_with(N, 7) - end_with(N - M, 7);
            int nine   = end_with(N, 9) - end_with(N - M, 9);

            int ans = 1;
            ans *= remain == 0 ? 1 : table[0][remain % 4];
            ans *= three  == 0 ? 1 : table[1][three  % 4];
            ans *= seven  == 0 ? 1 : table[2][seven  % 4];
            ans *= nine   == 0 ? 1 : table[3][nine   % 4];
            ans %= 10;

            out.println(ans);
        }
    }

    public int fact_prime(int n, int x){
        if (n == 0) return 0;
        return n / x + fact_prime(n / x, x);
    }

    public int fact_prime_iter(int n, int x){
        int res = 0;
        while (n > 0){
            res += n / x;
            n = n / x;
        }
        return res;
    }

    public int odd_end_with(int n, int x){
        if (n == 0) return 0;
        int res = n / 10;
        res += (n % 10 >= x ? 1 : 0);
        return res + odd_end_with(n / 5, x);
    }

    public int end_with(int n, int x){
        if (n == 0) return 0;
        return end_with(n / 2, x) + odd_end_with(n, x);
    }

    FastScanner in;
    PrintWriter out;

    void run() throws IOException {
        boolean oj;
        try {
            oj = ! System.getProperty("user.dir").equals("F:\\java_workspace\\leetcode");
        } catch (Exception e) {
            oj = System.getProperty("ONLINE_JUDGE") != null;
        }

        InputStream is = oj ? System.in : new FileInputStream(new File(INPUT));
        in = new FastScanner(is);
        out = new PrintWriter(System.out);
        long s = System.currentTimeMillis();
        solve();
        out.flush();
        if (!oj){
            System.out.println("[" + (System.currentTimeMillis() - s) + "ms]");
        }
    }

    public boolean more(){
        return in.hasNext();
    }

    public int ni(){
        return in.nextInt();
    }

    public long nl(){
        return in.nextLong();
    }

    public double nd(){
        return in.nextDouble();
    }

    public String ns(){
        return in.nextString();
    }

    public char nc(){
        return in.nextChar();
    }

    class FastScanner {
        BufferedReader br;
        StringTokenizer st;
        boolean hasNext;

        public FastScanner(InputStream is) throws IOException {
            br = new BufferedReader(new InputStreamReader(is));
            hasNext = true;
        }

        public String nextToken() {
            while (st == null || !st.hasMoreTokens()) {
                try {
                    st = new StringTokenizer(br.readLine());
                } catch (Exception e) {
                    hasNext = false;
                    return "##";
                }
            }
            return st.nextToken();
        }

        String next = null;
        public boolean hasNext(){
            next = nextToken();
            return hasNext;
        }

        public int nextInt() {
            if (next == null){
                hasNext();
            }
            String more = next;
            next = null;
            return Integer.parseInt(more);
        }

        public long nextLong() {
            if (next == null){
                hasNext();
            }
            String more = next;
            next = null;
            return Long.parseLong(more);
        }

        public double nextDouble() {
            if (next == null){
                hasNext();
            }
            String more = next;
            next = null;
            return Double.parseDouble(more);
        }

        public String nextString(){
            if (next == null){
                hasNext();
            }
            String more = next;
            next = null;
            return more;
        }

        public char nextChar(){
            if (next == null){
                hasNext();
            }
            String more = next;
            next = null;
            return more.charAt(0);
        }
    }
}
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2017年08月29日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 挑战程序竞赛系列(38):4.1模运算的世界(1)
    • POJ 1150: The Last Non-zero Digit
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档