其实上周只要做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(i−1,j)+f(i,j−1)f(i−1,j)f(i,j−1)i−1>0,j−1>0j−1≤0i−1≤0
完整注释代码如下:
#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<aiaj≥ai
起始条件也就是 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=0∑9f(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.跳跳
平面上给定了一些整点(横纵坐标均为整数的点),被称为 “魔法阵”。魔法少女派派想要在各魔法阵之间传送,每一次传送,她将使用下面的方式:
- 刚开始,派派已经位于某传送阵之上;
- 如果派派掌握一种魔法 (A,B),其中 A,B 均为整数。使用一次这个魔法可以让派派从任意整点 (X,Y)瞬间移动至 (X+A,Y+B);
- 选择一种魔法并开始传送,在一次传送过程中可以使用多次该魔法,但在抵达下一个传送阵之前仅能使用这一种魔法。
问派派至少需要掌握多少种魔法,才能在从任意魔法阵直接传送到任意魔法阵?
输入格式
第一行一个整数 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种情况进行分析,就可以知道最终的结果:
- 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序列
我们称一个字符串为好字符串,指这个字符串中只包含0
和1
。
现在有一个好字符串,求这个字符串中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。我们可以通过 push
和 pop
操作,将这 n 个元素依次放入栈中,然后从栈中弹出,依次把出栈的元素写下来得到的序列就是出栈序列。
比如 n=3,如果执行 push 1, push 2, pop, push 3, pop, pop
,那么我们 pop
操作得到的元素依次是 2,3,1。也就是说出栈序列就是 2,3,1。
现在给定一个合法的出栈序列,请输出一个合法的由 push
和 pop
操作构成的操作序列。这里要求 push
操作一定是按 1,2,…,n 的顺序。
输入格式
第一行一个整数 n。接下来一行 n 个整数,表示出栈序列。
输出格式
输出 2 n 行,每行一个 push
或 pop
操作,可以证明一个出栈序列对应的操作序列是唯一的。
样例输入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的由字符B
和W
组成的字符串,代表网格正方形的颜色。
输出格式
如果网格正确,请打印数字 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;
}