• 算法笔记_052:蓝桥杯练习Multithreading(Java)


    目录

    1 问题描述

    2 解决方案

     


    1 问题描述

    问题描述
      现有如下一个算法:
      repeat ni times
      yi := y
      y := yi+1
      end repeat
      令n[1]为你需要算加法的第一个数字,n[2]为第二个,...n[N]为第N个数字(N为需要算加法的数字个数),
      并令y初始值为0,先令i=1运行这个算法(如上所示,重复n[i]次),然后令i=2运行这个算法。。直到i=N。注意y值一直不要清零。最后y的值就是你需要的加法答案。
      你想知道,有没有某种运算顺序能使答案等于W。
      一个循环中的全部语句,是不能改变在总的语句排列中的相对顺序的。
      (这里的第i个循环是指这n[i]*2条语句。就是你把属于第i个循环的语句抽出来看,它们需要按照原顺序排列。在你没有运行完这个循环的最靠前一条未完成的 语句的时候,你是不能跳过它先去完成这个循环后面的语句的。你能做的仅是把若干个循环按照你所规定的顺序“归并”起来。)
      举个例子,n[1]= 2 ,n[2]=1, W=1.一种可行的运算顺序是“2 1 1 1 1 2”,数字为几表示运行第几个算法的下一条语句(你可以看到”1”出现了4次,是因为n[1]=2即循环两次,而每次循环里面有两条语句,所以2*2=4次)

      y值 y[1] 值 y[2] 值
    执行0条语句过后 0 0 0
    执行1条过后(y[2]=y) 0 0 0
    执行2条过后(y[1]=y) 0 0 0
    执行3条过后(y=y[1]+1) 1 0 0
    执行4条过后(y[1]=y) 1 1 0
    执行5条过后(y=y[1]+1) 2 1 0
    执行6条过后(y=y[2]+1) 1 1 0
           
      可以看到,最后y值变成了1,也就完成了我们的任务。
    输入格式
      第一行你会得到用空格分开的两个整数N(1<=N<=100)和W(-10^9<=W<=10^9),(N为需要算加法的数字个数,W是你希望算出的数)。
      第二行你会得到n个整数n[i] (1<=n[i]<=1000).
    输出格式
      第一行您应该输出Yes(若能以某种顺序使得这个算法得出W的值) 或No。
      如果第一行是No,接下来就不用继续输出了。
      如果是Yes, 请在第2行输出2*sigma(n[i])个用空格隔开的数,表示任意一种满足条件的运算顺序。
    样例输入
    1 10
    11
    样例输出
    No
    样例输入
    2 3
    4 4
    样例输出
    Yes
    1 1 2 1 2 2 2 2 2 1 2 1 1 1 1 2
    样例输入
    3 6
    1 2 3
    样例输出
    Yes
    1 1 2 2 2 2 3 3 3 3 3 3
    数据规模和约定
      对于30%的数据,n<=4, n[i]的和小于10.
      对于100%的数据,n<=100 , -10^9<=W<=10^9, 1<=n[i]<=1000

     


    2 解决方案

    初步看到此题,题意没看明白,前后仔细看了三四遍才整明白此题题意,竟无语吟噎......

     

    此题意思一个形象的理解:输入n个数:N[0]N[1]...N[n-1],和一个对照结果数w。把其中每一个N[i]看成一个进程,而N[i]具体值看成是这个进程所包含的线程数。例如,N[2] = 6,进程212个线程,如果结果输出Yes,则必定要输出122。为什么是122呢?因为执行一次循环,里面包含2个语句,6*2 = 12,所以进程2包含12个线程,且这12个线程的中间可以执行其它进程(例如:N[3])的线程,但是这12个线程的执行先后顺序不能打乱。不管有多少个进程,最后一个线程一定是执行y = y[i] + 1;即本题要求我们输出当y == w时的所有线程的一种可能执行顺序。

     

    y[i] = y; y = y[i] + 1;..执行规律可知,n个进程执行返回的最大ymaxY = N[0] + N[2] + ... +N[n-1]。(PS:因此,执行一次完整的循环,即执行两个语句,y的值自增1,且y的值可以向下传递)

     

    那么,到这里,请看一种特例:当N[1] = 1时,y[1] = y = 0; ...(PS:除了进程1之外的所有进程均执行完毕)...,y = y[1] + 1 = 1,从而得到最后y的结果为1

    此时,我们可以初步想到y的结果一定满足 1 <= y <=maxY

    由题意中给定的w值输入范围,可以轻松得到大部分输出No的情况。

     

    分析到这里,相信大家已经对于此题有了初步编码框架:即对w值进行划定区间,然后判断并找到相应输出结果。

     

    但是,本文也有一点遗憾,在蓝桥杯练习系统里最终的测评分为95分,其中的测试数据5运行错误,由于没有VIP特权,不能查看输入和输出测试数据,我自己也查看了代码逻辑好大一会也没有检查出具体逻辑错误>~<。(PS:在本文最后给出的参考资料链接中,那位网友的代最终码运行结果是100分,大家也不妨参考一下~)

     

    下面具体代码中判断Yes的情况核心思想:

    先执行count = (w-2)*2个线程,返回此时的进程数i,这时可以轻松得知temp[i] = w -2,再执行除去进程i的所有线程数目总和减去2的结果的连续的线程,再执行一次进程i中的线程,这时y = temp[i]+1=w-1,再执行一次按照前后顺序排列的线程倒数第2个线程,出现temp[N.length-1] = y =w-1,然后执行完进程i中剩下的线程,最后执行按照前后顺序排列的线程的最后一个线程,返回y =  temp[N.length-1]+1 =w

     

    如果没看懂上面所说思想,下面代码注释应该能够看明白。

     

    具体代码如下:

    package com.liuzhen.systemExe;
    
    
    import java.util.Scanner;
    
    
    public class Main{
        //返回数组N中的最小元素数组下标
        public int getMinI(int[] N) {
            int min = 0;
            for(int i = 0;i < N.length;i++) {
                if(N[i] < N[min])
                    min = i;
            }
            return min;
        }
        //执行第i个进程中的一个线程,执行完后,总线程数减1
        public void runThread1(int[] N, int i) {
            System.out.print((i+1)+" ");
            N[i]--;
        }
        //从第i个进程开始,到第n个进程结束,顺序执行其中的每一个线程
        public void runThread2(int[] N, int i, int n) {
            for( ;i <= n;i++) {
                while(N[i] > 0) {  //执行第i个进程中的每一个线程
                    System.out.print((i+1)+" ");
                    N[i]--;
                }
            }
        }
        
        public void printResult(int[] N, int w) {
            int maxY = 0;         //计算N个进程,依次执行能够得到的最大y值
            for(int i = 0;i < N.length;i++) {
                maxY += N[i];
                N[i] *= 2;      //此处乘以2表示,第i个线程需要执行的语句个数(因为执行一次循环,有两条语句)    
            }
            
            //(1)当w <= 0时,输出No
            if(w <= 0)
                System.out.println("No");
            //(2)当w == 1时,此时要看N[i]的最小值,才能决定输入Yes或者No
            else if(w == 1) {
                int min = getMinI(N);
                if(N[min] == 2) {  //由于前面N[i] *= 2,此处N[min] == 2表示执行一次循环需要执行两句
                    System.out.println("Yes");
                    runThread1(N,min);       //执行一次进程min中的第一个线程,此时y[0] = 0 
                    if(min == 0)             //执行完成除了进程min之外的所有进程
                        runThread2(N, 1, N.length-1);
                    else {
                        runThread2(N, 0, min-1);
                        runThread2(N, min+1, N.length-1);
                    }
                    runThread1(N,min);       //执行进程min中的第二个线程,此次y = y[0] + 1 = 1
                } else {
                    System.out.println("No");
                }
                
            }
            //(3)当数组N长度为1时,w不等于N[0]执行完整循环次数,输出No
            else if(N.length == 1 && w > 1 && w < maxY) {
                System.out.println("No");
            } 
            //(4)当数组N长度为1时,w等于N[0]执行完整循环次数,输出Yes
            else if(N.length == 1 && w == maxY) {
                System.out.println("Yes");
                while(N[0] > 0)
                    runThread1(N, 0);
            }
            //(5)当数组N的长度不等1且1 < w <= maxY时,此时一定会输出Yes
            else if(N.length != 1 && w > 1 && w <= maxY) {
                System.out.println("Yes");
                /*
                 * 下面代码注解中tempY[i]表示题目中第i个进程的yi
                 * y表示题目循环中的y
                 */
                int count = (w-2)*2;   
                if(count == 0) {
                    int tempI = 0;
                    for(int i = 0;i < N.length-1;i++) {
                        if(N[i] >= 4) {  //表示N[i]至少能够进行完整的2次循环,共四个语句
                            tempI = i;
                            break;
                        }        
                    }
                    runThread1(N, tempI);      //执行1次,此时tempY[tempI] = 0
                    if(tempI == 0)             
                        runThread2(N, 1, N.length-2);
                    else {
                        runThread2(N, 0, tempI-1);
                        runThread2(N, tempI+1, N.length-2);
                    }
                    while(N[N.length-1] > 2) {
                        runThread1(N, N.length-1);
                    }
                    runThread1(N, tempI);       //此时,y = tempY[tempI] + 1 = 1
                    runThread1(N, N.length-1);  //此时,tempY[N.length-1] = y = 1
                    while(N[tempI] > 0) {       //执行完N[tempI]中剩余的线程
                        runThread1(N, tempI);     
                    }
                    runThread1(N, N.length-1);   //此时执行最后一个线程,y = tempY[N.length-1] + 1 = 2
                } else {
                    int i = -1;
                     //从第0个进程开始,执行到第i个进程,当tempY[i] = w-3, y = w - 2时结束
                    while(count > 0) {
                        i++;
                         while(N[i] > 0) {
                             runThread1(N, i);    //此处执行两次runThread1表示执行一次整个循环,y值便会自增1
                            runThread1(N, i);
                            count = count - 2;
                            if(count == 0)
                                break;
                        }
                     }
                    runThread1(N, i);          //执行完此句,此时tempY[i] = w - 2
                    runThread2(N, i+1, N.length-2);    //执行进程i+1到 N.length-2之间的所有线程
                    while(N[N.length-1] > 2) {    //执行最后一个进程中的线程
                        runThread1(N, N.length-1);
                    }
                    runThread1(N, i);         //执行完此句,此时y = tempY[i] + 1 = w - 1
                    runThread1(N, N.length-1);  //执行完此句,此时tempY[N.length-1] = y =  w - 1
                    while(N[i] > 0) {     //执行完进程i中剩余的线程
                        runThread1(N, i);
                    }
                    runThread1(N, N.length-1);   //这是最后一个线程,执行完后,y = tempY[N.length-1] + 1 = w
                }
            }
            //(6)当w > maxY时,输出No
            else if(w > maxY) {
                System.out.println("No");
            }
        }
    
        
        public static void main(String[] args){
            Main test = new Main(); 
            Scanner in = new Scanner(System.in);
        //    System.out.println("请输入一个数字n和一个数字w:");
            int n = in.nextInt();
            int w = in.nextInt();
        //    System.out.println("请输入n个数字:");
            int[] N = new int[n];
            for(int i = 0;i < n;i++)
                N[i] = in.nextInt();
            test.printResult(N, w);
            
        }
    }

    运行结果:

    请输入一个数字n和一个数字w:
    2 2
    请输入n个数字:
    2 2
    Yes
    1 2 2 1 2 1 1 2 
    
    
    请输入一个数字n和一个数字w:
    3 6
    请输入n个数字:
    1 2 3
    Yes
    1 1 2 2 2 2 3 3 3 3 3 3 3 

    参考资料:

       1. 蓝桥杯练习系统Multithreading

  • 相关阅读:
    Mysql系列(十二)—— 索引下推优化
    Mysql系列(十一)—— 性能分析其他常用监控
    套路篇(一)之软件配置
    Mysql中的变量
    Mysql系列(十一)—— 性能分析慢查询日志
    Mysql系列(十)—— 性能分析工具profiling
    Mysql系列(九)—— 性能分析explain执行计划
    secure-file-priv特性
    Levenberg-Marquardt优化和zipf分布
    关于ADM和高维空间下距离度量的问题
  • 原文地址:https://www.cnblogs.com/liuzhen1995/p/6441758.html
Copyright © 2020-2023  润新知