蓝桥杯 历届试题 矩阵翻硬币 java

柔情痞子 提交于 2020-02-14 19:46:15

资源限制

时间限制:1.0s
内存限制:256.0MB

问题描述

小明先把硬币摆成了一个 n 行 m 列的矩阵。
随后,小明对每一个硬币分别进行一次 Q 操作。
对第x行第y列的硬币进行 Q 操作的定义:将所有第 ix 行,第 jy 列的硬币进行翻转。其中i和j为任意使操作可行的正整数,行号和列号都是从1开始。
当小明对所有硬币都进行了一次 Q 操作后,他发现了一个奇迹——所有硬币均为正面朝上。
小明想知道最开始有多少枚硬币是反面朝上的。于是,他向他的好朋友小M寻求帮助。
聪明的小M告诉小明,只需要对所有硬币再进行一次Q操作,即可恢复到最开始的状态。然而小明很懒,不愿意照做。于是小明希望你给出他更好的方法。帮他计算出答案。

输入格式

输入数据包含一行,两个正整数 n m,含义见题目描述。

输出格式

输出一个正整数,表示最开始有多少枚硬币是反面朝上的。

样例输入

2 3

样例输出

1

数据规模和约定

对于10%的数据,n、m <= 10^3;
对于20%的数据,n、m <= 10^7;
对于40%的数据,n、m <= 10^15;
对于10%的数据,n、m <= 10^1000(10的1000次方)。

.
.

解题思路

1、 从题目得知,如果一个硬币被翻转了奇数次后为正面朝上,那么它原始的状态一定是反面朝上。因此,我们需要统计所有翻转了奇数次硬币的个数。
2、 题目中的 “ 对第x行第y列的硬币进行 Q 操作的定义:将所有第 ix 行,第 jy 列的硬币进行翻转 ”。我们可以试着逆向思索,对于一个横坐标为X的硬币而言,我们翻转哪些硬币会影响到它而使它翻转呢?由此我们可以得出,当翻转的硬币的横坐标为X的约数时,会影响到它的翻转。比如,X=9,那么翻转横坐标为 1、3、9的时候会影响到它的翻转。纵坐标情况同理。
  对于一个硬币,我们必须考虑到它的横坐标和纵坐标。假如,此硬币的横坐标翻转了5次,纵坐标翻转了6次,那么它总的翻转次数为 5 * 6 = 30 次。因此我们得到一个公式:总翻转次数 (count) = 横坐标翻转次数 (count_x) * 纵坐标翻转次数 (count_y)。我们一开始就指出,我们需要找到翻转了奇数次的硬币,因此,横坐标翻转次数和纵坐标翻转次数均为奇数时,总翻转次数才为奇数次。
3、 接下来我们需要考虑,哪些数有奇数个约数呢?答案是完全平方数。它为 1,4,9,16,25,36… 即n的2次方,n为从1开始的正整数。
   从题目中得知,此时是一个 n * m 的矩阵,行号和列号都是从1开始。因此我们需要解决 1 到 n 之间完全平方数个数的问题。方法是求出sqrt(n),然后对它取整,即 1 - n 之间总共有 (int)(sqrt(n)) 个完全平方数。因此,反面朝上硬币的个数为横纵坐标完全平方数个数相乘,即 (int)((sqrt(n)) * (sqrt(m))) 。
4、 由于此题的数据是超大规模的。因此,我们需要解决大数开方、大数相乘和大数比较等问题。
  通常使用 String 来接受键盘输入大数,因为它的长度比较容易控制。而使用 java.math.BigInteger 包中的 BigInteger 类来存储大数据。它的原则是,只要计算机有足够的的内存,它就能存储多长位数的数。

大数开方: 牛顿逼近法。
  如果一个数的位数为偶数,那么这个数的方根就有 n/2 位,如果一个数的位数为奇数,这个数的方根就有 n/2 + 1 位。
比如 num=1000 ,那么它的位数为 4 ,即方根就有 2 位。我们从方根的最高位进行枚举。
          先枚举出它的十位:
            10 * 10 = 100 < 1000
            20 * 20 = 400 < 1000
            30 * 30 = 900 < 1000
            40 * 40 = 1600 > 1000
          则这个根的十位为 3 。
          再枚举它的个位:
            31 * 31 = 961 < 1000
            32 * 32 = 1024 > 1000
          则这个根的个位为 1 。即这个方根为 31 。
 
满分代码如下

import java.math.*;
import java.util.Arrays;
import java.util.Scanner;
public class BigNum {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String s1 = sc.nextLine();
        String s2 = sc.nextLine();
        System.out.println(bigSqrt(s1).multiply(bigSqrt(s2)));
    }
    private static BigInteger bigSqrt(String num) {
         int length = num.length();     //被开方数的位数
         int sqrt_len = 0;            //开方数的位数
         if(length % 2 == 0){
             sqrt_len = length / 2;
         }else{
             sqrt_len = length / 2 + 1;
         }
         BigInteger beSqrtNum = new BigInteger(num);
         char[] ch = new char[sqrt_len];            //记录开方数,开房数在数组中逆序存放
         Arrays.fill(ch, '0');                      //将ch数组初始化为'0'
         for(int i = 0; i < sqrt_len; i++){         //从开房数的最高位开始计算,使 每一位都转化为 开方数的平方且不大于被开方数
             for(char j = '1'; j <= '9'; j++ ){
                 ch[i] = j;
                 String s = String.valueOf(ch);
                 BigInteger sqrtNum = new BigInteger(s);
                 BigInteger squareNum = sqrtNum.multiply(sqrtNum);
                 if(squareNum.compareTo(beSqrtNum) == 1){     //大数比较问题
                     ch[i] -= 1;
                     break;
                 }
             }
         }
         return new BigInteger(String.valueOf(ch));
     }
}
利用冒泡循环,通过60%实例
import java.util.Arrays;
import java.util.Scanner;
public class Main {
	static int[][] a;
	public static void main(String[] args) {
		Scanner input = new Scanner(System.in);
		long sum = 0;
		int n = input.nextInt();
		a=new int [n][2];
		for(int i =0;i<n;i++) {
			a[i][0]=input.nextInt();
		}
		for(int i=0;i<n-1;i++)
		{
			boolean f=true;
			for(int j=0;j<n-1-i;j++)
			{
				if(a[j][0]>a[j+1][0])
				{
					temp(j,j+1);
					a[j][1]+=1;
					sum+=a[j][1];
					a[j+1][1]+=1;
					sum+=a[j+1][1];
					f=false;
				}
			}
			if(f) break;
		}
		System.out.println(sum);
	}
	static void temp(int x,int y)
	{
		int t0 = a[x][0];
		int t1 = a[x][1];
		a[x][0] = a[y][0];
		a[x][1] = a[y][1];
		a[y][0] = t0;
		a[y][1] = t1;
	}
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!