2022南京工程学院程序设计及应用竞赛——个人题解

314 阅读8分钟

第一题

计算自然数n至自然数m之间(含n、m)所有整数中的各位数字是奇数的和。如输入n是110,m是118,则结果为1+1+1+1+1+1+1+1+1+3+1+1+1+1+5+1+1+1+1+7+1+1=34。

签到题,循环模拟即可。

public class p1 {

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        int n, m;
        int ans = 0;

        n = sc.nextInt();
        m = sc.nextInt();
        sc.close();

        p1 p = new p1();

        for (int i = n; i <= m; i++) {
            ans += p.oddAdd(i);
        }

        System.out.println(ans);
    }

    public int oddAdd(int x) {
        int i = 0;
        while (x > 0) {
            if ((x % 10) % 2 == 1)
                i += x % 10;
            x = x / 10;
        }
        return i;
    }
}

第二题

当你输入信用卡号码的时候,有没有担心输错了而造成损失呢?其实可以不必这么担心,因为并不是一个随便的信用卡号码都是合法的,它必须通过Luhn算法来验证通过。

该校验的过程:

1、从卡号最后一位数字开始,逆向将奇数位(1、3、5等等)相加。

2、从卡号最后一位数字开始,逆向将偶数位数字,先乘以2(如果乘积为两位数,则将其减去9),再求和。

3、将奇数位总和加上偶数位总和,结果应该可以被10整除。

例如,卡号是:5432123456788881

则奇数、偶数位(用斜体标出)分布:5 43 21 23 45 67 88 8 8 1

奇数位和=35

偶数位乘以2(有些要减去9)的结果:1 6 2 6 1 5 7 7,求和=35。

最后35+35=70 可以被10整除,认定校验通过。

请编写一个程序,从键盘输入卡号,然后判断是否校验通过。通过显示:“成功”,否则显示“失败”。

比如,用户输入:356827027232780

程序输出:成功

签到题,分奇偶循环模拟即可。

public class p2 {
   public static void main(String[] args) {
      Scanner sc = new Scanner(System.in);
      String str = sc.nextLine();
      sc.close();

      int sum = 0;
      for (int i = str.length() - 1; i >= 0; i -= 2) {
         sum += Integer.parseInt(str.charAt(i) + "");
      }

      for (int i = str.length() - 2; i >= 0; i -= 2) {
         int temp = Integer.parseInt(str.charAt(i) + "") * 2;
         sum += temp > 9 ? temp - 9 : temp;
      }

      if (sum % 10 == 0)
         System.out.println("成功");
      else
         System.out.println("失败");
   }
}

第三题

整数的分划问题。

如,对于正整数n=6,可以分划为:

6

5+1

4+2, 4+1+1

3+3, 3+2+1, 3+1+1+1

2+2+2, 2+2+1+1, 2+1+1+1+1

1+1+1+1+1+1+1

现在的问题是,对于给定的正整数n,编写算法打印所有划分。

用户从键盘输入 n (范围1~10)

程序输出该整数的所有划分。

递归枚举入门题,关于重复的情况我是做了一个判断,比较当前整数剩余部分和上一个值的大小,循环从较小值开始倒序的话,就不会重复了。

public class p3 {
   public static void main(String[] args) {
      Scanner sc = new Scanner(System.in);
      int n = sc.nextInt();
      sc.close();

      p3 p = new p3();

      String ans = "";
      p.divide(n, n, ans);
   }

   public void divide(int n, int prev, String ans) {
      if (n == 0)
         System.out.println(ans);
      else {
         for (int i = prev < n ? prev : n; i > 0; i--) {
            if (ans.isEmpty()) {
               divide(n - i, i, ans + i);
            } else {
               divide(n - i, i, ans + "+" + i);
            }
         }
      }
   }
}

第四题

给定一个长度为n的正整数数列A1,A2,... , An 和一个非负整数x,给定m次查询, 每次询问能否从区间[k, r]中选择两个数使得它们的异或等于x。

输入格式:

输入第一行包含三个整数n,m,x。

第二行包含n个正整数A1,A2,...,An。

接下来m行,每行两个整数k,r表示询问区间[k, r]。

输出格式:

对于每个查询, 如果该区间内存在两个数的异或为x则输出yes, 否则输出no。

输入样例:

4 4 1

1 2 3 4

1 4

1 2

2 3

3 3

输出样例:

yes

no

yes

no

难点在于异或,Java我不记得有现成方法,那么就得手动实现,寻找的时候做双循环,判断一下一致性就行。

public class p4 {
   public static void main(String[] args) {
      Scanner sc = new Scanner(System.in);
      int n = sc.nextInt();
      int m = sc.nextInt();
      int x = sc.nextInt();

      p4 p = new p4();

      int[] array = new int[n];

      for (int i = 0; i < n; i++) {
         array[i] = sc.nextInt();
      }

      for (int i = 0; i < m; i++) {
         int a = sc.nextInt() - 1;
         int b = sc.nextInt() - 1;
         boolean suc = false;
         for (int j = a; j <= b; j++) {
            for (int k = a; k <= b; k++) {
               if (j != k && p.xor(array[j], array[k]) == x) {
                  System.out.println("yes");
                  suc = true;
                  break;
               }
            }
            if (suc)
               break;
         }

         if (!suc) {
            System.out.println("no");
         }
      }
      sc.close();
   }

   public int xor(int a, int b) {
      int i = 0;
      int sum = 0;
      while (a > 0 || b > 0) {
         if (a % 2 == 0 && b % 2 == 1 || a % 2 == 1 && b % 2 == 0)
            sum += Math.pow(2, i);
         a /= 2;
         b /= 2;
         i++;
      }
      return sum;
   }
}

第五题

学校大门外长度为L的马路上有一排树,每两棵相邻的树之间的间隔都是1米。可以把马路看成一个数轴,马路的一端在数轴0的位置,另一端在L的位置,数轴上的每个整数点(即0,1,2,……,L)都有一棵树。由于马路上有一些区域要用来建地铁。这些区域用它们在数轴上的起始点和终止点表示。已知任意区域的起始点和终止点的坐标都是整数,区域之间可能有重合的部分。现在要把这些区域中的树(包括区域端点处的两棵树)移走。请计算将这些树都移走后,马路上还有多少棵树。

输入

500 3

150 300

100 200

470 471

输出

298

(表中输入数据说明:第一行第一个表示区间的总长度,第二个数表示有几个需要移动的区域:例如500 3,表示区间的总长度为[0,500],需要移动的是3个区间,这三个区间分别是[150,300]、[100,200]、[470,471])

比较巧妙的一题,不过也是模版了。

可能有人想到如下的做法:

使用一个可以覆盖所有区间范围的数组,对每个区间进行标记,结果为数组中被标记元素的个数。这种方法的时间复杂度是O(nm)。 注:n是区间个数,m是所有区间总的范围。

但这样的话,时间复杂度其实比较低了,一种更优的思路如下:

使用一个可以覆盖所有区间范围的数组flg,初始化时将数组中的元素都置为0。对于每一个区间[l,r],将flg[l]++,flg[r+1]--。最后使用一个累加器cnt,初始置为0。依次扫描数组中的每一个元素,对于第i个元素,cnt+=flg[i]。此时,若cnt>0,则说明i在某些区间中;若cnt==0,则证明i不在任何区间中。统计cnt==0的元素个数即可。

public class p5 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int l = sc.nextInt();
        int n = sc.nextInt();

        int cnt = 0, ret = 0;
        int[] flg = new int[l+1];

        for (int i = 0; i < n; i++) {
            int a = sc.nextInt();
            int b = sc.nextInt();
            flg[a]++;
            flg[b + 1]--;
        }
        for (int i = 0; i <= l; i++) {
            cnt += flg[i];
            if (cnt == 0) {
                ret++;
            }
        }

        System.out.println(ret);
        sc.close();
    }
}

第六题

一种简单的文件传输加密系统:假设明文一共有m个字节,加密后的密文第1字节和第m字节互换位置,第2字节和第m-2字节互换位置,第3字节和第m-3 字节互换位置,以此类推。互换位置后,每一个字节的第1和第8比特,第2和第7比特,第3和第6比特,第4和第五比特互换位置。为了验证程序正确,输入一个字符串,加密一次后以整数格式输出每个字符,再加密一次后,以整数格式和字符串格式输出第二次加密的内容。

测试用例:

输入“abcde”,则运行结果如下:

edcba

-90 38 -58 70 -122

abcde

97 98 99 100 101

这题其实有点迷惑人,关键在于他题目描述的不清不楚,由于我写这道题的时候时间不多了,所以直接放弃掉了最后一题,考虑到大概率是人工阅卷,这题就按照规范一点的写法完成了。

难点在于对二进制位的处理,这里我直接按照计算逻辑硬写了。

public class p6 {
   public static void main(String[] args) {
      Scanner sc = new Scanner(System.in);
      String str = sc.nextLine();
      sc.close();

      p6 p = new p6();

      String newStr = p.stringReverse(str);
      for (int i = 0; i < newStr.length(); i++) {
         short temp = (short) newStr.charAt(i);
         System.out.print(temp + " ");
      }
      System.out.println();
      String newnewStr = p.stringReverse(newStr);
      System.out.println(newnewStr);
      for (int i = 0; i < newnewStr.length(); i++) {
         short temp = (short) newnewStr.charAt(i);
         System.out.print(temp + " ");
      }
   }

   public short bitReverse(int n) {
      short i = 7;
      short sum = 0;
      boolean minus;
      if (n % 2 == 1)
         minus = true;
      else
         minus = false;
      n /= 2;
      i--;
      while (n > 0) {
         if (n % 2 == 1)
            sum += Math.pow(2, i);
         n /= 2;
         i--;
      }
      if (minus)
         sum -= 128;
      return sum;
   }

   public String stringReverse(String str) {
      String newStr = new String();
      for (int i = str.length() - 1; i >= 0; i--) {
         short tempInt = bitReverse(str.charAt(i));
         char temp = (char) tempInt;
         newStr += temp;
      }
      return newStr;
   }
}

第七题

小蓝学习了最短路径之后特别高兴,他定义了一个特别的图,希望找到图中的最短路径。

小蓝的图由2021 个结点组成,依次编号1 至2021。

对于两个不同的结点a, b,如果a 和b 的差的绝对值大于21,则两个结点之间没有边相连;如果a 和b 的差的绝对值小于等于21,则两个点之间有一条长度为a 和b 的最小公倍数的无向边相连。

例如:结点1 和结点23 之间没有边相连;结点3 和结点24 之间有一条无向边,长度为24;结点15 和结点25 之间有一条无向边,长度为75。

请计算,结点1 和结点2021 之间的最短路径长度是多少。

这题眼熟,我拿蓝桥杯国二那年,省赛填空题压轴就是这个,这里就不给题解了,各位想了解的话直接看往年题解就行了。