蓝桥杯 历年试题 矩阵翻硬币

[蓝桥杯] 历年试题 矩阵翻硬币

最近报名了蓝桥杯,应该是我学习编程以来的第一场正式比赛了,最近就刷了一下历年试题 这是其中比较有趣的题目,不过我的答案只有70%的分,想看正确代码可以看这里,没过是算法不太行,思路还是可以看看的。

问题描述

小明先把硬币摆成了一个 n 行 m 列的矩阵。
随后,小明对每一个硬币分别进行一次 Q 操作。

对第x行第y列的硬币进行 Q 操作的定义:将所有第 ix 行,第 jy 列的硬币进行翻转。其中i和j为任意使操作可行的正整数,行号和列号都是从1开始。

当小明对所有硬币都进行了一次 Q 操作后,他发现了一个奇迹——所有硬币均为正面朝上。

小明想知道最开始有多少枚硬币是反面朝上的。于是,他向他的好朋友小M寻求帮助。

聪明的小M告诉小明,只需要对所有硬币再进行一次Q操作,即可恢复到最开始的状态。然而小明很懒,不愿意照做。于是小明希望你给出他更好的方法。帮他计算出答案。

题目分析

首先,对于一个硬币,如果翻了奇数次后正面向上,则说明它一开始是反面朝上的,偶数反之,故题目就是叫我们求对所有硬币进行了Q操作后有多少个硬币是翻了奇数次的。
然后,对Q操作进行分析。Q操作是

将所有第 i*x 行,第 j *y 列的硬币进行翻转。其中i和j为任意使操作可行的正整数,行号和列号都是从1开始

看上去有点像埃氏筛,盲猜和约数有关,然后按题意模拟一下n = 4 ,m = 6 的情况,用数组a[i][j],存一下结果,以下是模拟结果。

1 2 2 3 2 4
2 4 4 6 4 8
2 4 4 6 4 8
3 6 6 9 6 12

从模拟发现
1)a[i][j] = a[i][1] * a[1][j]
2)a[1][j] = j的约数个数,a[i][1] = i的约数个数

这个具体证明我就不写了。因为我们只要知道奇偶关系,所以可以考虑用1带表奇数,0带表偶数,将a[i][j] 里的值都用01替换掉后就将题目转换为求所有值的和了。因为a[i][j] = a[i][1] * a[1][j](将值替换后这规律依旧没变),所以全部数据的和可以用(a[1][1] +a[2][1] +…a[n][1])*(a[1][1]+a[1][2]+…a[1][m]) 表示。
因为用01替换了奇偶,所以(a[1][1] +a[2][1] +…a[n][1])就是a[i][1] 中奇数个数,结合发现1,问题转化为:求1到m中,有几个数的约数是奇数。一个数的约数是成对出现的,如果是k是j的约数则j/k也是,约数是奇数个,说明j存在一个约数k使k = j/k ,即j是平方数,所以问题又转换为:1到m有几个平方数,思考即可得答案为[sqrt(m)] (m的平方根向下取整)

所以最终答案就是m的平方根向下取整乘以n的平方根向下取整

本以为到这里就完了,没想到只是另一个开始,数据范围是1000位数,然后算平方根。。。你这是难为刚学编程的我啊,我用了一下高精度加二分,然后稍微优化,过了70%,以下是带码:

#include<iostream>
#include<string>
#include<algorithm>
#include<cmath>
#include<vector>
using namespace std;

vector<int>A,B,C,Mid2,D,E;

void print(vector<int>& A){
    for(int i=A.size()-1;i>=0;i--)printf("%d",A[i]);
}

int com(vector<int>& A,vector<int>& B){//A>B 1     A<B -1     A==B 0
	if(A.size()!=B.size())return A.size()>B.size()?1:-1;
	for(int i=A.size()-1;i>=0;i--)if(A[i]!=B[i])return A[i]>B[i]?1:-1;
	return 0;
}

//高精度除以低精度 A/b=C...r  A>=0,b>0
vector<int>div(vector<int>& A,int b,int& r)
{
    vector<int>C;
    r=0;
    for(int i=A.size()-1;i>=0;i--)
    {
        r=r*10+A[i];
        C.push_back(r/b);
        r%=b;
    }
   
    reverse(C.begin(),C.end());
    while(C.size()>1&&C.back()==0)C.pop_back();
   
    return C;
}

//高精度加法 C=A+B  A>=0,B>=0
vector<int>add(vector<int>& A,vector<int>& B){
//	printf("add ");print(A);printf(" ");print(B);printf(" ");
    if(B.size()>A.size())return add(B,A);
    vector<int>K;
    for(int i=0,t=0;i<A.size()||t;i++){
        if(i<A.size())t+=A[i];
        if(i<B.size())t+=B[i];
        K.push_back(t%10);
        t=t/10;
    }
//    printf("ans=");print(K);printf("\n");
    return K;
}

//高精度乘以低精度 C=A*b  A>=0,b>=0
vector<int>mul(vector<int>& A,int b){
    vector<int>C;
    for(int i=0,t=0;i<A.size()||t;i++){
        if(i<A.size())t+=A[i]*b;
        C.push_back(t%10);
        t/=10;
    }
    while(C.size()>1&&C.back()==0)C.pop_back();
   
    return C;
}

//高精度乘以高精度 C=A*B A>=0,B>=0,A.size()==B.size()
vector<int>Mul(vector<int>&A,vector<int>&B)
{
	vector<int>C,t1,t2;
	C.push_back(0);
	for(int i=A.size()-1;i>=0;i--)
	{
		t1=mul(C,10);
		t2=mul(B,A[i]);
		C=add(t1,t2);
	}
	while(C.size()>1&&C.back()==0)C.pop_back();
   
    return C;
}

//高精度减法 C=A-B   A>=B,A>=0,B>=0
vector<int>sub(vector<int>& A,vector<int>& B)
{
    vector<int>C;
    for(int i=0,t=0;i<A.size();i++){
        t=A[i]-t;
        if(i<B.size())t-=B[i];
        C.push_back((t+10)%10);
        if(t<0)t=1;
        else t=0;
    }
    while(C.size()>1&&C.back()==0)C.pop_back();
   
    return C;
}

vector<int>vsqrt(vector<int>& A){//A>1
	vector<int>L,R,one,Mid;
	int r;
	if(A.size()>10){
		int len=A.size()/2;
		for(int i=1;i<len-1;i++)L.push_back(0);
		L.push_back(1);
		for(int i=1;i<=len+1;i++)R.push_back(0);
		R.push_back(1);
	}else{
		L.push_back(0);
		R=div(A,2,r);
	}
//	printf("%d %d\n",L.size(),R.size());
//	print(L);
	one.push_back(1);
	while(com(R,L)==1){
		vector<int>C=add(R,L);
		Mid=div(C,2,r);
		Mid2=Mul(Mid,Mid);
		if(com(Mid2,A)==1)R=Mid;
		else if(com(Mid2,A)==-1)L=add(Mid,one);
		else break;
	}
	if(com(R,L)==1)return Mid;
	C=sub(R,one);
	Mid2=Mul(C,C);
	while(com(Mid2,A)!=1){
		C=add(C,one);
		Mid2=Mul(C,C);
	}
	return sub(C,one);
}


int main(){//70% 暴力二分 运行超时 
    char ch;
    vector<int>C,D,E; 
    while((ch=getchar())>='0'&&ch<='9')A.push_back(ch-'0');
    reverse(A.begin(),A.end());
    while((ch=getchar())>='0'&&ch<='9')B.push_back(ch-'0');
    reverse(B.begin(),B.end());

    C=vsqrt(A);
    D=vsqrt(B);
//	printf("\nC.size=%d %d\n",C.size(),D.size());
	
	E=Mul(C,D);
    print(E);
    
	return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值