第二周题解

其实上周只要做8道题目,所以允许我偷个懒,将上周的第9,10道题c v 过来 (qwq)

1.路径计数

有一个n×n的网格,有些格子是可以通行的,有些格子是障碍。

一开始你在左上角的位置,你可以每一步往下或者往右走,问有多少种走到右下角的方案。

由于答案很大,输出对10^9+7取模的结果。

输入格式

第一行一个正整数n。

接下来n行,每行n个正整数,1表示可以通行,0表示不能通行。

输出格式

一个整数,表示答案。

样例输入

3
1 1 1
1 0 1
1 1 1

样例输出

2

数据规模

对于100%的数据,保证2≤n≤100,左上角右下角都是可以通行的。

一开始,我以为这是一道搜素回溯题。但是看到了这里最大的数据规模,100,我就知道这道题不能有dfs做了。那么就只能改变思路。

我们可以设 f[i] [j] 表示到达点(i,j)的方法数,那么因为只能向下和向右走,所以状态转移方程也很容易了:
f ( i , j ) = { f ( i − 1 , j ) + f ( i , j − 1 ) i − 1 > 0 , j − 1 > 0 f ( i − 1 , j ) j − 1 ≤ 0 f ( i , j − 1 ) i − 1 ≤ 0 f(i,j)= \begin{cases} f(i-1,j)+f(i,j-1) & i-1>0,j-1>0\\ f(i-1,j) & j-1 \leq 0 \\ f(i,j-1) & i-1 \leq 0 \end{cases} f(i,j)= f(i1,j)+f(i,j1)f(i1,j)f(i,j1)i1>0,j1>0j10i10
完整注释代码如下:

#include<bits/stdc++.h>
using namespace std;
#define mod 1000000007   //求余
long long n,numarr[105][105],f[105][105];
int main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			cin>>numarr[i][j];
		}
	}
	f[1][1]=1;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			if(numarr[i][j]){
				if(i-1>0&&j-1>0){
					f[i][j]=(f[i][j-1]%mod)+(f[i-1][j]%mod);  //要求余储存
				}
				else if(i-1==0&&j-1>0){
					f[i][j]=(f[i][j-1]%mod);    //求余
				}
				else if(j-1==0&&i-1>0){
					f[i][j]=(f[i-1][j]%mod);   //状态转移,求余
				}				
			}
		}
	}
	cout<<f[n][n]%mod;  //记得求余
	return 0;
} 

2.最大和上升子序列

给定一个长度为 n 的数组 a_1,a_2,…,a_n,问其中的和最大的上升子序列。也就是说,我们要找到数组 p1,p2,…,pm1,2,…,,满足 1≤p_1<p_2<⋯<p_m≤n并且 a_p_1<a_p_2<⋯<a_p_m,使得a_p_1+a_p_2+⋯+a_p_m最大。

输入格式

第一行一个数字 n。

接下来一行 n 个整数 a_1,a__2,…,a_n。

输出格式

一个数,表示答案。

样例输入

6
3 7 4 2 6 8

样例输出

21

数据规模

所有数据保证 1≤n≤1000,1≤ai≤10^5

又是一道动态规划的题目(怎么这么多动态规划题目啊?小声bb),我们可以先设 f[i] 为以第 i 个数为结尾的最大和上升子序列的最大和。

那么我们就可以从前向后推出动态转移方程了:
f ( i ) = { m a x ( f ( j ) + a i , f ( i ) ) a j < a i a i a j ≥ a i f(i)= \begin{cases} max(f(j)+a_i,f(i)) & a_j<a_i\\ a_i & a_j \geq a_i \end{cases} f(i)={max(f(j)+ai,f(i))aiaj<aiajai
起始条件也就是 f(1)=a_1 了,这道题1000的数据量,时间复杂度显然是够过的了。

完整代码如下:

#include<bits/stdc++.h>
using namespace std;
long long n,numarr[1005],f[1005];
int main(){
	long long ans,max1=0;   //要开long long
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>numarr[i];
	}
	f[1]=numarr[1];   //初始条件
	for(int i=2;i<=n;i++){
			for(int a=1;a<i;a++){
				if(numarr[a]<numarr[i]){
					ans=f[a]+numarr[i];  
					f[i]=max(f[i],ans);  //状态转移
				}
			}	
			if(!f[i]){
				f[i]=numarr[i];   //状态转移
			}		
	}
	for(int i=1;i<=n;i++){
		if(f[i]>max1){
			max1=f[i];  //找到最大值
		}
	}
	cout<<max1;  //输出结果
	return 0;
}

3.加一

给定一个整数 n。你需要对它做 m 次操作。在一次操作中,你要将这个数的每一位 d 替换成 d+1。比如,1912在进行一次操作后将变成 21023。

请求出整数 n 进行了 m 次操作后的长度。答案可能很大,输出对 10^9+7 取模后的结果。

输入格式

第一行一个整数 t,表示测试单元的个数。

接下来 t 行,每行有两个整数 n 和 m,表示最初的数字和进行多少次操作。

输出格式

对于每个测试单元输出最终数字的长度,答案对 10^9+7 取模。

样例输入

5
1912 1
5 6
999 1
88 2
12 100

样例输出

5
2
6
4
2115

数据规模

所有数据保证 1≤t≤2⋅10^5, 1≤n≤10^9, 1≤m≤2⋅10^5

这道题我一开始的想法就是想找规律,最后发现没有任何规律,(qwq),最后也是了解了思路,并且在尝试了无数次,(失败了无数次后)才成功的。以下就是我的做法:

我们可以知道将一开始的数字分为几个单独的数字,然后对单个数字,计算他们在操作了 m 次后的长度为多少,然后再相加即可。

可是我们该怎么知道一个数字在操作了 m 次后的长度为多少呢?

我们可以用动态规划的思想。

我们可以假设 f[i] [j] 为一个数在操作了 j 次后,数字 i 的个数,那么我们在知道操作 k 次后,各个数字的个数后,那么我们肯定也能知道在操作 i+1 次后的各个数字的个数。

下面是状态转移方程:
f ( i + 1 , j + 1 ) + = f ( i , j ) ,     i < 9 f ( 0 , j + 1 ) + = f ( 9 , j ) , f ( 1 , j + 1 ) + = f ( 9 , j ) ,    i = 9 f(i+1,j+1)+=f(i,j) ,\,\ i<9 \\ f(0,j+1)+=f(9,j),f(1,j+1)+=f(9,j) ,\,\, i=9 f(i+1,j+1)+=f(i,j), i<9f(0,j+1)+=f(9,j),f(1,j+1)+=f(9,j),i=9
那么这个数在处理 m 次后的长度就为 :
∑ i = 0 9 f ( i , m ) \sum_{i=0}^{9}f(i,m) i=09f(i,m)
这就是预处理的过程,我们可以知道 m 的范围最大为20000,所以我们要预先计算m从0200000,然后把09分别在操作 0~200000 结果存在一个数组里面,这样我们在输入处理时就只要通过查询这个数组即可知道答案了。

以下为完整代码:

#include<bits/stdc++.h>
using namespace std;
#define mod 1000000007
unsigned long long t,m_c,numarr[20],f[15][200005];  //f(i,j)表示在操作m次后,数字i的个数
unsigned long long book[10][200001];  //用一个二位数字存放预处理后的结果
inline void findnum(int x,int m){   //预处理函数
    f[x][0]=1;   //初始条件
    unsigned long long temp0,temp1,ans=0;
    for(int i=1;i<=m;i++){
        temp0=f[0][i-1],temp1=f[1][i-1];
        for(int a=9;a>=2;a--){   //一定要从大到小,并且0,1要特殊处理
            if(f[a][i-1]){
                if(a==9){
                    f[0][i]=(f[0][i]+f[a][i-1])%mod,f[1][i]=(f[1][i]+f[a][i-1])%mod;  //记得求余,状态转移
                }
                else{
                    f[a+1][i]=f[a][i-1];
                }
            }
        }
        f[1][i]=(f[1][i]+temp0)%mod,f[2][i]=(f[2][i]+temp1)%mod;  //0,1特殊处理
    }
    for(int a=1;a<=200000;a++){
        for(int i=0;i<=9;i++){
            ans=(ans+f[i][a])%mod;
        }      
        book[x][a]=ans;   //存放结果
        ans=0;      
    }
}
long long findans(int x,int m_c){
    int tot=0;
    long long ans=0;
    while(x>0){
        numarr[tot++]=x%10;
        x/=10;  //求每一位数
    }
    for(int i=0;i<tot;i++){
        ans=(ans+book[numarr[i]][m_c])%mod;   //求出结果
    }
    return ans;
}
int main(){
    int x;
        for(int a=0;a<=9;a++){
            findnum(a,200000);  //预处理
            memset(f,0,sizeof(f));  //记得在每次处理后都要将f数字归零
        }
    scanf("%d",&t);
       for(int i=1;i<=t;i++){
           scanf("%d%d",&x,&m_c);
           printf("%lld\n",findans(x,m_c));   //要用scanf和printf,否则会超时
       }
    return 0;
}

4.跳跳

平面上给定了一些整点(横纵坐标均为整数的点),被称为 “魔法阵”。魔法少女派派想要在各魔法阵之间传送,每一次传送,她将使用下面的方式:

  1. 刚开始,派派已经位于某传送阵之上;
  2. 如果派派掌握一种魔法 (A,B),其中 A,B 均为整数。使用一次这个魔法可以让派派从任意整点 (X,Y)瞬间移动至 (X+A,Y+B);
  3. 选择一种魔法并开始传送,在一次传送过程中可以使用多次该魔法,但在抵达下一个传送阵之前仅能使用这一种魔法

问派派至少需要掌握多少种魔法,才能在从任意魔法阵直接传送到任意魔法阵?

输入格式

第一行一个整数 N。

接下来一行 N 行,每行包含两个整数 X_i,Y_i, 表示每个魔法阵的坐标。

输出格式

一个数,表示答案。

样例1输入

3
1 1
4 5
1 4

样例1输出

6

解释: 任务是从 (1,1) 传送至 (4,5) 以及 (1,4) 、从 (4,5) 传送至 (1,1) 以及 (1,4) 、从 (1,4)) 传送至 (1,1)以及 (4,5)) 。

注意你不能使用 (0,3)+(3,1)的魔法从 (1,1) 到达 (4,5)。因为每次移动,你只能使用一种魔法。

当然,你可以学习 (0,1),那样的话,从 (1,1) 到达 (1,4) 则需要使用 3 次(0,1) 魔法了。

样例2输入

3
1 1
2 2
1000000000 1000000000

样例2输出

2

数据规模

  • N∈[10,500]
  • X_i,Y_i∈[0,10^9], 但保证坐标之间两两不同

这道题我们需要注意到一个非常重要的点。

就是,我们如果从一个点到另一个点可以一直使用一种魔法,这样可以缩小 A,B 的大小,减少我们使用魔法的种类。

例如:我们有一组数据:

3
1 1
2 2
4 4

其实我们很容易就可以看出来,我们只需要两种魔法,分别是(1,1)和(-1,-1)这是为什么呢?

其实我们从(1,1)到(2,2)需要(1,1),而从(1,1)到(4,4)需要(3,,3),那么我们就可以将(3,3)这个魔法变为使用3次(1,1),也就是我们可以将(A,B)魔法变为(A/C,B/C),其中,C也就是A和B的最大公因数,那么我们的思路就很清晰了,我们只需要将每个点到另一个点的魔法化简后,看看这个魔法之前有没有被用过,如果没有被用过就将ans++,并把这个魔法加入到队列中,最后,我们要将ans*2,因为我们也要反着再走一遍.

说了这么多,可能还是没有解释清楚,总之看代码吧:

#include<bits/stdc++.h>
using namespace std;
int n,tot;
struct point{
    int x;
    int y;
}parr[405],book[500000];   //book为标记是否该魔法有被用过
point magic;
inline void findbefore(int d_x,int d_y){
    for(int i=0;i<tot;i++){
        if(d_x==book[i].x&&d_y==book[i].y){   //如果这个魔法被用过就返回
            return;
        }
    }
    book[tot].x=d_x,book[tot++].y=d_y;  //否则就加入队列,并tot++
}
inline void turn(int x,int y){
    if(!x||!y){
        return;
    }    
    else{    
        x=abs(x),y=abs(y);  //取绝对值
        while(x){
            int temp=y%x;           
            y=x; 
            x=temp;   //辗转相除法求最大公因数
        }
        magic.x/=y,magic.y/=y;   //将魔法转化
    }
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d%d",&parr[i].x,&parr[i].y);   
    }
    for(int i=1;i<=n-1;i++){
        for(int j=i+1;j<=n;j++){
            if(parr[j].x>parr[i].x){
                magic.x=parr[j].x-parr[i].x,magic.y=parr[j].y-parr[i].y;   
            }
            else{
                magic.x=parr[i].x-parr[j].x,magic.y=parr[i].y-parr[j].y;  //计算初始魔法
            }
            turn(magic.x,magic.y);   //将魔法缩小
            findbefore(magic.x,magic.y);  //查找之前是否有使用过该魔法
        }
    }
    printf("%d",tot*2);   //输出结果
    return 0;
}

5.异或和或

对于一个长度为 n 的01序列 a_1,a_2,…,a_n。

你可以执行以下操作任意多次:

  • 选择两个下标 1≤i,j≤n(i≠j)。
  • 记x=a_i xor a_j , y=a_i or a_j , 其中 xor 表示按位异或 , or 表示按位或。
  • 然后令 a_i=x,a_j=y 或 a_i=y,a_j=x。

给定两个01序列 s,t , 请你判断是否可以通过有限次(可以为0次)操作将序列 s 变为 t。

输入格式

第一行一个整数 t , 表示数据的组数(1≤t≤10^3)。接下来 t 组数据:

每组第一行一个01字符串 s(1≤|s|≤10^3),每组第二行一个01字符串 t(1≤|t|≤10^3)。

注意:|s| 可能不等于 |t|。

输出格式

如果可以通过有限次(可以为0次)操作将序列 s 变为 t , 输出 YES , 否则输出 NO

样例输入

2
001
011
11
101

样例输出

YES
NO

样例解释

第一组数据选择 i=2,j=3, 那么 x=1,y=1 , 接着令 a_i=x,a_j=y,即可得到 t 序列。

第二组数据 |s|=2,|t|=3| 显然无法满足要求。

其实这是一道逻辑推理题,我们可以只需要对3种情况进行分析,就可以知道最终的结果:

  1. a_i=1,a_j=0

我们可以设对a_i=1,a_j=0的操作为1操作,那么,如果a_i=1,a_j=0,那么我们通过上述操作可以得到x=1,y=1,那么显然在操作后a_i=1,a_j=1,也就是,我们对01操作后可以将原本的0替换成1.

2. a_i=1,a_j=1

我们设该操作为2操作,那么我们通过上述操作后,可以得到x=0,y=1,那么我们就可以让a_i=0,a_j=1,或者a_j=1,a_i=0,那么这个2操作也就是将一个原本是1的位置变为0.

 3. a_i=0,a_j=0

我们设该操作为3操作,那么,我们通过上述操作可以得到x=0,y=0,显然这个3操作没有任何用处,00还是00

那么我们就可以总结出结论了:其实我们的 s 序列其实只要有至少1个1,那么它就可以表示出同样长度的除了全是0的序列。

因为如果我们1的个数少了,我们可以通过1操作来增加1,并且再通过有限次2操作将对应位置的1变为0,这样就可以表示出所有除了全是0的序列了.

知道了上述结论后,我们就可以很容易写出代码了,我们只需要先比较 s 序列和 t 序列的长度,如果 s 序列中有1,那么再判断 t 序列是否全为 0,如果不全为0,那么就肯定可以表示出来。

如果 s 序列是全为0的序列,我们只需判断 t 序列是否全为 0,如果不是,那么肯定就表示不出来(因为3操作是无用的操作)。

完整代码如下:

#include<bits/stdc++.h>
using namespace std;
bool judge(string str1,string str2){
	if(str1.size()!=str2.size()){
		return false;
	}
	int flag=0;
	for(int i=0;i<str1.size();i++){
		if(str1[i]=='1'){
			flag=1;
			break;
		}
	}
	if(flag){
		for(int i=0;i<str2.size();i++){
			if(str2[i]=='1'){
				return true;
			}
		}
		return false;
	}
	else{
		for(int i=0;i<str2.size();i++){
			if(str2[i]=='1'){
				return false;
			}
		}
		return true;
	}
}
int main(){
	int t;
	string str1,str2;
	cin>>t;
	for(int i=0;i<t;i++){
		cin>>str1>>str2;
		if(judge(str1,str2)){
			cout<<"YES"<<endl;
		}
		else{
			cout<<"NO"<<endl;
		}
	} 
	
	return 0;
} 

6. 01序列

我们称一个字符串为好字符串,指这个字符串中只包含01

现在有一个好字符串,求这个字符串中1恰好出现k次的子串有多少个。

输入格式

第一行给出一个数字k,表示子串中1的个数。

第二行给出好字符串。

输出格式

输出一个整数,表示好字符串中有多少个符合条件的子串

数据范围

0≤k≤10^6, |s|≤10^6

样例输入1

1
1010

样例输出1

6

样例输入2

2
01010

样例输出2

4

这道题我一开始是想用动态规划的思路的,但发现似乎找不到状态转移方程(也可能是因为我是个蒟蒻),所以我就换了一种思路。

下面是我用画图解释的思路:
在这里插入图片描述

注意,这个思路只适用于在k!=0的请况下,在k=0时,我们还需要另一种方法:

在这里插入图片描述

知道了这个思路,我们就可以很容易的写出代码了,完整代码如下:

#include<bits/stdc++.h>
using namespace std;	
string str;	
int ans1=1,ans2=1;
void findnum(int start,int endd){
	start--,endd++;
	while(str[start]!='1'&&start>=0){
		ans1++,start--;   //计算左侧0的数量
	}
	while(str[endd]!='1'&&endd<str.size()){
		ans2++,endd++;   //计算右侧0的数量
	}
}
unsigned long long findans(){   //k=0的情况
	unsigned long long num=0,t1,t2,ans=0;
	for(int i=0;i<str.size();i++){
		if(str[i]=='1'){
			ans+=((num+1)*num/2);   
			num=0;
		}
		else{
			num++;
		}
	}   
	ans+=((num+1)*num/2);   
	return ans;
}
int main(){
	unsigned long long k,start,endd,num=0,res=0;
	cin>>k;
	cin>>str;
	if(k==0){
		cout<<findans();   //k=0特殊处理
		return 0;
	}
	for(int i=0;i<str.size();i++){
		if(str[i]=='1'){
			start=i;   //找到左边的序号
			num++;		
			for(int j=start+1;j<str.size();j++){			
				if(num==k){
					endd=j-1;    //找到右边的序号
					break;
				}
				if(str[j]=='1'){
					num++;   //
				}
			}
			if(num==k){
				findnum(start,endd);   //计算左右0的个数			
				res+=(ans1*ans2),ans1=1,ans2=1;  //字串个数为(左边0的个数+1)*(右边0的个数+1)
			}	
			num=0;   //记得归零
		}
	}
	cout<<res;
	return 0;
} 

7.出栈序列判断

现在有一个栈,有 n 个元素,分别为 1,2,…,n。我们可以通过 pushpop 操作,将这 n 个元素依次放入栈中,然后从栈中弹出,依次把出栈的元素写下来得到的序列就是出栈序列。

比如 n=3,如果执行 push 1, push 2, pop, push 3, pop, pop,那么我们 pop 操作得到的元素依次是 2,3,1。也就是说出栈序列就是 2,3,1。

现在给定一个合法的出栈序列,请输出一个合法的由 pushpop 操作构成的操作序列。这里要求 push 操作一定是按 1,2,…,n 的顺序。

输入格式

第一行一个整数 n。接下来一行 n 个整数,表示出栈序列。

输出格式

输出 2 n 行,每行一个 pushpop 操作,可以证明一个出栈序列对应的操作序列是唯一的。

样例输入1

3
2 3 1

样例输出1

push 1
push 2
pop
push 3
pop
pop

样例输入2

5
1 3 5 4 2

样例输出2

push 1
pop
push 2
push 3
pop
push 4
push 5
pop
pop
pop

数据规模

对于 100% 的数据,保证 1≤n≤100000,输入一定是个合法的出栈序列。

这道题我们可以这么想,如果我们想让数字 k 出栈,那么我们的pop肯定要摆在这个数字的push下方,但是又不能就在它的下方,要不然可能会被其他的数字干扰,例如,我们可以模拟一次出栈操作:

在这里插入图片描述

当然上述操作的前提是这组数据是可行的,否则就要判断这组数据的可行性,当然,题目中的数据都是可行的,所以是可以采用上述方法的。

完整代码如下:

#include<bits/stdc++.h>
using namespace std;
int num[100005],endd,book[100005],n;   //book[i]用来记录push i 下方有几个pop
inline void add(int x){
	if(x>endd){
		book[x]++;
		endd=x;
	}
	else{
		book[endd]++;   //模拟操作
	}
}	
int main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		scanf("%d",&num[i]);
		add(num[i]);   //进行pop操作
	} 
	for(int i=1;i<=n;i++){
		if(!book[i]){
			printf("push %d\n",i);
		}
		else{
			printf("push %d\n",i);
			for(int j=1;j<=book[i];j++){
				printf("pop\n");
			}
		}   //最后打印输出
	}
	return 0;
} 

8.序列维护

你有一个序列,现在你要支持几种操作:

  • insert x y,在从前往后的第x个元素后面插入y这个数。如果x=0,那么就在开头插入。
  • delete x,删除从前往后的第x个元素。
  • query k,询问从前往后数第k个元素是多少。

输入格式

第一行一个整数m,表示操作个数。

接下来m行,每行一个上面所述的操作。

输出格式

输出若干行,对于每个查询操作,输出答案。

样例输入

10
insert 0 1
insert 1 2
query 1
query 2
insert 0 3
query 1
delete 1
query 1
insert 1 4 
query 2

样例输出

1
2
3
1
4

数据规模

对于100%的数据,保证m≤10^3

对于insert操作,保证1≤y≤10^9。

对于所有操作,保证位置不会超出当前序列的长度

这里的数据是10^3,那么我们如果用数字模拟插入和删除的话,那么肯定是要TLE的,那么我们就需要链表这个神器了。

链表对于插入和删除操作来说是非常容易的,但是就是查询速度有点慢,但是对于这里的数据(10^3),查询还是绰绰有余的,所以我们只需要写出一个可以查找,删除,和添加的链表就可以了

完整代码如下:

#include<bits/stdc++.h>
using namespace std;
int m;
string str;
struct node{
    int value;
    node *next;
};
void insert(int x,int y,node *head){   //插入
    node *p=head->next;
    int num=1;
    if(!x){
        node *newp=new node;
        newp->next=head->next;
        newp->value=y;
        head->next=newp;   //对于x=0要特殊处理
        return;
    }
    while(num<x&&p!=NULL){
        p=p->next;
        num++;
    }   //找到第x个数
    node * neww=new node;
    neww->value=y;
    neww->next=p->next;
    p->next=neww;   //插入y
}
void d(int x,node * head){   //删除
    int num=1;
    node*p=head->next;
    if(x==1){
        head->next=p->next;
        p->next=NULL;
        p->value=0;  //对于x=1特殊处理
        return;
    }
    while(num<x-1&&p!=NULL){
        p=p->next;
        num++;   //找到第x个数
    }
    node * p2=p->next;
    p->next=p2->next;
    p2->next=NULL;   //删除
    p2->value=0;
}
void q(int y,node *head){   //查找
    int num=1;
    node *p=head->next;
    if(y==1){
        cout<<p->value<<endl;   //对于y=1特殊处理
        return;
    }
    while(num<y&&p!=NULL){
        p=p->next;
        num++;   //找到第y个数
    }
    cout<<p->value<<endl;   //输出结果
}
int main(){
    int x,y;
    node* head=new node;
    head->next=NULL,head->value=0;   //设置头节点
    cin>>m;
    for(int i=0;i<m;i++){
        cin>>str;
        if(str=="insert"){
            cin>>x>>y;
            insert(x,y,head);  //插入
        }
        else if(str=="delete"){
            cin>>x;
            d(x,head);   //删除
        }
        else{
            cin>>y;
            q(y,head);   //查询
        }
    }
    return 0;
}

9.网格判断

您将获得一个 n×n 的网格,网格中每个正方形的颜色为黑色或白色。如果满足以下所有条件,则网格是正确的:

  • 每行的黑色方块数与白色方块数相同。
  • 每列的黑色正方形数与白色方块数相同。
  • 没有行或列具有 3 个及以上相同颜色的连续正方形。

给定网格,确定它是否正确。

输入格式

第一行一个数字 n(2≤n≤24), 并且数字 n 是偶数。

接下来 n 行,每行包含一个长度为n的由字符BW组成的字符串,代表网格正方形的颜色。

输出格式

如果网格正确,请打印数字 1 在一行上。否则,请打印数字 0 在一行上。

样例输入

4
WBBW
WBWB
BWWB
BWBW

样例输出

1

一道简单的模拟题,我们只需要按照题目的要求逐条判断即可,而且这里的数据量也很温柔,只有24,所以时间是绰绰有余的。

完整代码如下:

#include<bits/stdc++.h>
using namespace std;
char str[30][30];
int main(){
	int n,num_w=0,num_b=0,num=0;
	cin>>n;
	char flag;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			cin>>str[i][j];   //输入
		}
	}
	for(int i=1;i<=n;i++){
		flag=str[i][1];   //flag为当前的颜色
		for(int j=1;j<=n;j++){
			if(str[i][j]=='W'){
				num_w++;  //记录白方块的数量
			}
			else{
				num_b++;  //记录黑方块的数量
			}
			if(str[i][j]==flag){
				num++;  //记录有几个连续的黑或白方块
			}
			else{
				flag=str[i][j];
				num=1;  //如果不同就更换颜色,并把num重置
			}
			if(num==3){
				cout<<"0"<<endl;  //如果有连续3个就输出0
				return 0;
			}
		}
		if(num_w!=num_b){  //如果白方块数量不等于黑方块数量就输出0
			cout<<"0"<<endl;
			return 0;
		}
		num_w=0,num_b=0,num=0;  //记得重置
	}
	for(int j=1;j<=n;j++){   //下面同理:
		flag=str[1][j];
		for(int i=1;i<=n;i++){
			if(str[i][j]=='W'){
				num_w++;
			}
			else{
				num_b++;
			}
			if(str[i][j]==flag){
				num++;
			}
			else{
				flag=str[i][j];
				num=1;
			}
			if(num==3){
				cout<<"0"<<endl;
				return 0;
			}
		}
		if(num_w!=num_b){
			cout<<"0"<<endl;
			return 0;
		}
		num=0,num_w=0,num_b=0;
	}
	cout<<"1"<<endl;  //如果都通过就输出1
	return 0;
} 

10. 整齐的数组

Polycarp 有一个长度为 n 的数组 a_1,a_2,…,a_n(n 是偶数)。Polycarp 还得到了一个正整数 k,他开始对数组 a 做如下操作:选择一个下标 i (1≤i≤n) 使 a_i 减去 k。

在 Polycarp 进行若干次操作后(可能 0 次),数组 a中的所有数都变成相同的了。请你找到最大的符合要求的 k,如果 k 可以为任意大,请输出 −1。

输入格式

第一行一个整数 t,表示测试单元的个数。

接下来每个测试单元有两行。第一行包含一个偶数 n。第二行包含 n 个整数 a_1,a_2,…,a_n。

输出格式

对于每个测试单元输出单独一行一个整数 k (k≥1)—— Polycarp 能用来对数组进行操作的最大的数,或者 −1 —— 如果k 能任意大的话。

样例输入

3
6
1 5 3 1 1 5
8
-1 0 1 -1 0 1 -1 0
4
100 -1000 -1000 -1000

样例输出

2
1
1100

数据规模

所有数据保证 1≤t≤10,4≤n≤40(n 是偶数),−106≤a_i≤106,并且 n 的总和不超过100。

我们要通过有限次操作将这些数都变成一样的,那么我们要先找到这些数中最小的那个数。因为,我们最终的操作肯定是会将这 n 个数都变成最小的那个数的。然后我们需要计算出其他数距离最小的那个数还有多少,那么显然,最终的 k 也就是这些数的最大公因数了。如果这些数都相等,那么 k 也就可以取无穷大,输出-1即可。

完整代码如下:

#include<bits/stdc++.h>
using namespace std;
int t,num[45],n,ds[45],minn=1000005;
int findans(int x,int y){
	while(x){
		int temp=x;
		x=y%x;
		y=temp;		
	}   //辗转相除法求最大公因数
	return y;
}
int main(){
	cin>>t;
	int len=0;   //len表示除了最小数的其他数的个数
	for(int i=1;i<=t;i++){
		minn=1000005,len=0;  //minn设大一点,len要归零
		cin>>n;
		for(int j=1;j<=n;j++){
			cin>>num[j];
			if(num[j]<minn){
				minn=num[j];   //找最小数
			}	
		}
		for(int k=1;k<=n;k++){
			if(num[k]==minn){
				continue;
			}
			ds[len++]=num[k]-minn;   //求差距
		}
		if(!len){
			cout<<"-1"<<endl;   //如果len=0,代表所有数都相等,输出-1
			continue;
		}
		for(int a=0;a<=len-2;a++){
			ds[a+1]=findans(ds[a],ds[a+1]);	  //找这些数的最大公因数
		}
		cout<<ds[len-1]<<endl;  //输出结果
	}
	return 0;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值