zoukankan      html  css  js  c++  java
  • 蓝桥杯试题:矩阵翻硬币

    矩阵翻硬币

     这是蓝桥杯的一道练习题,题目如下:

    问题描述
      小明先把硬币摆成了一个 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次方)。

     

    思路:

      对于坐标为(x,y)的硬币来说,当被翻转的次数为奇数时,它的朝向会被改变。被翻转的次数等于x的约数个数乘以y的约数个数。而翻转次数为奇数的充要条件时x和y的约数都是奇数,即x,y都是完全平方数。
      对于矩阵nm,其中横纵坐标皆为完全平方数的硬币个数为:sqrt(n)sqrt(m)。


      这道题的另一个难点是求超大整数的平方根。网上一个比较经典的算法是:
      对于一个整数num,其位数为n,若n为奇数,则其平方根的位数length = (n+1)/2;若n为偶数,则其平方根的位数length = n/2。 我们从1000...000(有length-1个0)的最高位开始,若这个数的平方小于num,则将当前位的数值加1;若这个数的平方大于num,则将当前位的数值减1,并开始分析下一位。


      计算平方时,免不了需要自己实现大数的乘法。同样是参考网上的例子,算法如下:
      首先使用char数组存储整数,用int类型的数组ret暂时保存结果,整数的低位存在数组的低位。对输入的两个数组x和y,将其每一位x[i]和y[j]分别相乘,将数值添加到ret[i+y]中。
      现在,ret的的每个元素可能是大于10的,对于每一个元素的数值,我们将个位数保存,将十位数进位。
      ret[i+1] += ret[i] / 10;  
      ret[i] = ret[i] * 10;
      最后,将int类型的数组转化为char类型并返回。
      主要算法就是这些。

    代码:

    import java.util.Scanner;
    
    public class Main {
    	public static void main(String[] args){
    		Scanner scan = new Scanner(System.in);
    		char[] n = null,m = null;
    		char[] temp = {1,2,3};
    		if(scan.hasNext())
    			n = scan.next().toCharArray();
    		if(scan.hasNext())
    			m = scan.next().toCharArray();
    		n = ReverseOrder(n);
    		m = ReverseOrder(m);
    		char[] ret = reverse(n,m);
    		
    		int length = ret.length;
    		while(length>1 && ret[length-1] == '0')
    			length--;
    		for(int i=length-1; i>=0; i--)
    			System.out.print(ret[i]);
    	}
    	
    	/**
    	 * 使数组倒序排列  
    	 */
    	private static char[] ReverseOrder(char[] a){
    		for(int i=0; i<(a.length/2); i++){
    			char temp = a[i];
    			a[i] = a[a.length-1-i];
    			a[a.length-1-i] = temp;
    		}		
    		return a;
    	}
    	
    	/**
    	 * 按照游戏规则翻转矩阵中的硬币
    	 */
    	private static char[] reverse(char[] n, char[] m){
    		char[] sqrt_n = sqrt(n);
    		char[] sqrt_m = sqrt(m);
    		return mult(sqrt_n,sqrt_m);
    	}
    	
    	/**
    	 * 求大数的平方根(向下取整)
    	 */
    	private static char[] sqrt(char[] a){		
    		int length = (a.length%2 == 1)? a.length/2+1 : a.length/2;
    		char[] ret = new char[length];
    		for(int i=0; i<length-1; i++)
    			ret[i]='0';
    		ret[length-1] = '1';
    		for(int i=length-1; i>=0; i--){
    			while(ret[i] <= '9'){
    				if(compare(a,mult(ret,ret)) != 1){
    					ret[i] = (char) (ret[i] - 1);
    					break;
    				}
    				if(ret[i] < '9')
    					ret[i] = (char) (ret[i] + 1);
    				else
    					break;
    			}
    		}
    		return ret;
    	}
    	/**
    	 * 求大数的乘积
    	 */
    	private static char[] mult(char[] a, char[] b){
    		int[] number = new int[a.length+b.length];
    		for(int i=0;i<number.length;i++)  number[i]=0;
    		int a_start = 0;
    		while(a[a_start]=='0')
    			a_start++;
    		int b_start = 0;
    		while(b[b_start]=='0')
    			b_start++;
    		for(int i=a_start; i<a.length; i++)
    			for(int j=b_start; j<b.length; j++){
    				number[i+j] += (a[i]-'0') * (b[j]-'0');
    			}
    		for(int k=a_start+b_start; k<number.length-1; k++){
    			number[k+1] += number[k]/10;
    			number[k] = number[k] % 10;
    		}
    		
    		char[] ret;
    		if(number[number.length-1]==0)
    			ret = new char[number.length-1];
    		else
    			ret = new char[number.length];
    		for(int i=0; i<ret.length; i++){
    			ret[i] = (char) (number[i] + '0');
    		}
    		return ret;
    	}
       /** 
    	*  比较两个大数的大小
    	* @return 
    	* -1 : 数组a的值更大;
    	*  0 : 两个数组的值相同;
    	*  1 : 数组a中的值更小;
    	*/
    	private static int compare(char[] a, char[] b){
    		if(a.length > b.length)
    			return 1;
    		if(a.length < b.length)
    			return -1;
    		for(int i = a.length-1; i>=0; i--){
    			if(a[i] > b[i])
    				return 1;
    			if(a[i] < b[i])
    				return -1;
    		}
    		return 0;
    	}
    }
    
  • 相关阅读:
    题解 LNOI2014 LCA
    题解 P3413 【SAC#1
    题解 P3372 【【模板】线段树 1】(珂朵莉树解法)
    题解 P2610 【[ZJOI2012]旅游】
    题解 CF911D 【Inversion Counting】
    题解 CF1037D 【Valid BFS?】
    bootstrap常用部件下载
    sql获取上月同期
    VSS配置
    SQL中的union,except,intersect用法
  • 原文地址:https://www.cnblogs.com/yuex/p/5522642.html
Copyright © 2011-2022 走看看