提示:个人思路
题目
一、最大矩形c++题解
题目:给定一个仅包含 0 和 1 、大小为 rows * cols 的二维二进制矩阵 matrix,找出只包含 1 的最大矩形,并返回其面积。
输入输出格式
输入格式
输入一个仅包含 0 和 1 、大小为 rows * cols 的二维二进制矩阵(列表)。
输出格式
输出只包含 1 的最大矩形面积的整数结果。
输入输出样例1
输入
[[“1”,“0”,“1”,“0”,“0”],
[“1”,“0”,“1”,“1”,“1”],
[“1”,“1”,“1”,“1”,“1”],
[“1”,“0”,“0”,“1”,“0”]]
1.思路
- 这道题可以使用暴力和动态规划,我会简单介绍一下动态规划的解法,以这道题具体为例。来做,接下来我会简单介绍一下动态规划是什么。
- 动态规划简单来说就是我们利用历史数据去推出新的数据来,减少我们的计算量。
- 我也不会着重介绍,最优子结构跟重复子问题,我就简单讲一下,怎么解题? 动态规划的题可以分为三部:
- 第一步我们要确定我们数组里的每个元素代表的是什么含义 第二步,我们要确定初始值该怎么设立 第三步,我们要确定如何用历史数据去推出新的数据
- 我们以这道题为例来看。
- 首先,我们要确定我们说的这个数组是什么含义?我这里设定一个二维数组,它的含义主要有两个,dp[i][j]代表在第i行中包含第j个元素的最长的连续’1’的长度。例如[1,1,0,1,1],相应的dp矩阵的值为[1,2,0,1,2]
- 第二步要设定初始值,但是这道题比较特殊,我根据dp数组的之前的元素和输入的题目给定的那个矩阵初来进行判定的,这个在第三步里,我会一起做到。
- 最后一步,这里设置状态转移方程。首先我要连初始化一起完成,所以我先判断第i行j列的元素是否等于一,如果等于一的话,我还要判断他是不是第一列?如果是第一列的话,则它的长度就是一,如果不是第一列的话,它的长度就是dp[i][j-1]的长度加上一.反之,如果第i行j列的元素是零的话,这个位置dp[i][j]=0就可以,因为他打断了连续长度
if(a[i][j]==1)
dp[i][j]= j==0?1:dp[i][j-1]+1;
else
dp[i][j]=0;
- 接下来我开始介绍我具体的思路,这道题首先他会输入一行字符串,然后我首先判断三个最特殊的情况,分别是空字符串只有一个0的字符串和只有一个1字符串分别输出它们的结果.
- 如果都不是,则我进行接下来的操作,我首先将这些字符串里面的数字都提取出来,存放到一个数组当中,但是由于题里没有告诉我有几行几列,所以我要手动计算,只要我遇到一个反中括号就记行数加一。证明这一行输入完,当我输入完第一行的时候,通过一个字符要占四个字符位(“字符”,)的特征,我算出了一行有多少个元素
- 这样我就得到了行数跟列数。但由于字符串最外围还有一个反中括号,所以最后我的行数要减去一。
下一步开始遍历整个矩阵,遍历每一个元素的同时计算最大面积,最大面积的计算原理是这样的.
假设dp[0][5]的值为5,只考虑第i行为高,时能组成的最大矩阵面积为5,高为1,底为dp[0][5] 。 - 再考虑下一行,此情况下能组成的最大矩形的高为2,底为min(dp[0][5],dp[0][4])
- 就这样把第一到第i行,再从第二行第i行.第三到第i行如此全部便利完
- 紧接着再去便利第二列,如此把所有的列也全都便利完。
- 通过上面的计算方式,考虑了所有的能够组成的矩形,能够找出最大矩形面积,其实也算是暴力法,不过利用了dp数组保存下来的信息可以减少很多冗余计算。
2.代码
```cpp
#include<bits/stdc++.h>
using namespace std;
int main(){
string s;
int k=0,max=0,min=0,ls=100000,hs=0;
int a[100][100],dp[100][100];
getline(cin,s);//输入一行字符串
if(s=="[]"||s=="[0]")//判断最特殊的三种情况
cout<<0;
else if(s=="[1]")
cout<<1;
else{
for(int i=0;i<s.length();i++){//退出条件为将整个字符串全部分割完成
if(s[i]==']')//判断一共有多少行
hs++;
if(s[i]==']'&&hs==1)//判断一行有多少个元素,即有多少列
ls=(i-1)/4;
if(s[i]=='1')
a[hs][k++]=1;//将字符串类型转化为整型
if(s[i]=='0')
a[hs][k++]=0;
k=k%ls;//类似循环数组,控制列数
}
hs--;
for(int i=0;i<hs;i++)
for(int j=0;j<ls;j++)//初始化整个dp数组的值
{if(a[i][j]==1)
dp[i][j]= j==0?1:dp[i][j-1]+1;
else
dp[i][j]=0;}
for(int j=ls-1;j>=0;j--)//从最后一列到第零列,遍历所有的列数
for(int i=0;i<hs;i++)//这两个循环是控制第一行到第四行,第二行到第四行,如此一直便利下去
{ min=dp[i][j];
for(int z=i;z<hs;z++)
{
if(dp[z][j]==0)//如果有一个数等于零了,它的最小值已经为零,所以矩形的面积为零,直接跳过本次循环
continue;
if(min>dp[z][j])//选出已经计算的该列的行数内最小的那个数
min=dp[z][j];
if(min*(z-i+1)>max)//计算最大矩形面积
max=min*(z-i+1);
}}
cout<<max;
}
return 0;
}
二、莫比乌斯反演
题目:小明正在参加一次数学考试,现在他遇到了这样一道难题:给定 5 个整数:a,b,c,d,k,你需要在 a 到 b 之间找到一个整数 x,在 c 到 d 之间找到一个整数 y,使得 gcd(x,y)=k。gcd(x,y) 表示 x 和 y 的最大公约数。由于选择的数量可能非常大,您只需要输出不同数对的总数。
(x=5,y=7) 和 (x=7,y=5) 被认为是相同的。
假设 a=c=1 始终成立。
输入输出格式:
输入格式
输入包含五个整数 a,b,c,d,k,含义如题目描述所示。整数之间以空格间隔。
输出格式
针对输入,打印出不同数对的总数。
输入输出样例1
输入
1 3 1 5 1
输出
9
1.思路
- 这道题思路本身并不是很难,最简单的思路,其实就是暴力便利,定义i,从一开始,小于等于b,定义j也是从一开始小于等于d(因为题里说了a=c=1
始终成立)。 - 如果我们直接计算两个数的最大公约数,去跟k做比较的话,他的时间会超时,所以我们要通过其他的办法,优化我们的算法.
- 在这里我们要用到求最小公倍数的一个数学公式就是(gcd(a,b)lcm(a,b)=ab)(两个数最小公倍数乘以两个数最大公约数等于两个数相乘),所以由上面数约公式我们可以得到lcm(a,b)=a*b/gcd(a,b)(两个数最小公倍数等于两个数乘积除以两个数最大公约数)。
- 在循环中我加了三重判断。我们先假设i和j的最大公约数就是k,我们做除法得到最小公倍数,然后分别对i和j取余,判断是否能整除它们。
- 如果不能整除他们,我们就知道这样计算的结果不是最小公倍数,从而推理得k不是这个i和j的最大公约数。
如果整除,我们还要考虑最小公倍数一定大于或等于i和j中最大的那个数。 - 这条也满足的情况下,只能说明这样得出的结果,刚好可以整除i和j,但并不能说明他一定就是最小公倍数,所以我们在使用欧基里德算法,计算i和j的最大公约数,判断是否等于k,如果三个条件全部满足,我们让计数变量加一,最后输出这个计数变量即可
2.代码
#include<bits/stdc++.h>
using namespace std;
int gcd(int a,int b){//欧几里德算法计算两个数的最大公约数
if(b==0)
return a;
return gcd(b,a%b);
}
int main(){
int a,b,c,d,k;
int sum=0;
cin>>a>>b>>c>>d>>k;
for(int i=1;i<=b;i++)//考虑两个范围内,所有的组合情况
for(int j=i;j<=d;j++)
{
if( ((i*j)/k)%i==0 && ((i*j)/k)%j==0)//先假设i和j的最大公约数就是k,我们做除法得到最小公倍数,然后分别对i和j取余,判断是否能整除它们
if( ((i*j)/k)>=i && ((i*j)/k)>=j )//最小公倍数一定大于或等于`i`和`j`中最大的那个数
if(gcd(i,j)==k)//计算`i`和`j`的最大公约数,判断是否等于`k`
sum++;
}
cout<<sum;
return 0;
}
三、昆虫繁殖
题目:XXXXX 科学家在热带森林中发现了一种特殊的昆虫,这种昆虫的繁殖能力很强。每对成虫过 x 个月产 y 对卵,每对卵要过两个月长成成虫。假设每个成虫不死,第一个月只有一对成虫,且卵长成成虫后的第一个月不产卵(过 X 个月产卵),问过 Z 个月以后,共有成虫多少对?
输入输出格式
输入格式
x ,y , z 的数值。
输出格式
过 Z 个月以后,共有成虫对数。
输入输出样例1
输入
1 2 8
输出
37
1.思路
- 这道题其实就是斐波那契数列的扩展。
菲波那契数列源自于一个问题,就是一个月有一对刚出生的兔子,第二个月进入成熟期,第三个月开始生,一对兔子会生一对兔子,兔子永不死去,n个月以后有多少兔? - 大部分的同学肯定对这个问题很耳熟,他就是从第三项开始数列里的,每一项等于前两项之和他其实运用的就是动态规划的思想,接下来我会简单介绍一下动态规划是什么。
- 动态规划简单来说就是我们利用历史数据去推出新的数据来,减少我们的计算量。
我也不会着重介绍,最优子结构跟重复子问题,我就简单讲一下,怎么解题? - 动态规划的题可以分为三部:
- 第一步我们要确定我们数组里的每个元素代表的是什么含义
- 第二步,我们要确定初始值该怎么设立?
- 第三步,我们要确定如何用历史数据去推出新的数据
我们以这道题为例来看。首先,我们要确定我们说的这个数组是什么含义?我这里的表是b[i]代表的,就是第i个月有多少对成虫。第一步就完成了 - 第二步要设定初始值,根据题意,我们可以知道,在前x月成虫的数量都是一对儿,然后因为过了x月生的卵,还要两个月才能成熟。所以,前x+2个月都是一对成虫,设b[1]~b[x+2]全为一。
- 最后一步也是最麻烦的一步,我们要找到历史数据和新数据之间的关系,通过题意,我们可以得知我第i月的成虫数量是b[i-1]月成虫的数量,加上x-2个月之前的成虫数量乘以y,因为我这个月的虫虫其实是我前x-2(X月产完卵以后还要两个月成熟,一次产y对)月产下来的卵。
- 所以我们就推出了公式b[i]=b[i-1]+y*b[i-2-x],但还要注意一点,题里明确的说是过完第一个月,所以我们的循环条件判断内里要往上加一,最后,输出结果即可
2.代码
a = input()
a = a.split() #以空格作为分隔符将字符串分割成一个列表
a = [int(i) for i in a] #这一部是将a列表中的所有元素转化为,整数类型,然后形成一个新的列表返回
b = [0] #第0个月有0对成虫
for i in range(1, a[0] + 3): #设`b[1]~b[x+2]`全为一
b.append(1)
for i in range(a[0]+3,a[2]+2): #第`i`月的成虫数量是i-1月成虫的数量,加上x-2个月之前的成虫数量乘以`y`
b.append(b[i-1]+a[1]*b[i-2-a[0]])
print(b[-1]) #最后一个月的成虫数