A.最大面积
题目链接:登录—专业IT笔试面试备考平台_牛客网
签到题,选两个最短边最小,注意数据类型long long就行
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
int main()
{
ll a,b,c,d;
cin>>a>>b>>c>>d;
ll a1=min(a,c);
ll h1=min(b,d);
cout<<a1*h1<<endl;
return 0;
}
B.种树
题目链接:登录—专业IT笔试面试备考平台_牛客网
分类讨论:
1.如果全都种满了树则不需要再种树 输出 0。
2.因为种树操作是从一棵树开始可以种到任意一个位置的,贪心一下就是种到底,所以如果编号为1或者编号为n的土地有树那么一天就能完成输出 1。
3.如果编号为1、n的土地均没有树,那么最少则需要两天时间才能完成操作。
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxl=1e5+10;
int main()
{
int n;cin>>n;
string s;cin>>s;
int ans;
bool flag=true;
for(int i=0;i<n;i++){
if(s[i]=='0')flag=false;
}
if(flag)ans=0;
else if(s[0]=='1'||s[n-1]=='1')ans=1;
else ans=2;
cout<<ans<<endl;
return 0;
}
C.奇怪的电梯
题目链接:登录—专业IT笔试面试备考平台_牛客网
这题wa太多发了,没想到本层到本层的话直接可以到。
思路:这题不能死脑筋直接算一步移动的区间,因为题目未说明电梯只能走一步,电梯也是可以分段走的。
明确这点我们来分析一下:
1.如果a-k>1说明电梯能到最底层,电梯能到最底层就一定能到最高层(k<n);
2.如果a+k<n说明电梯能到最底层,电梯能到最高层就一定能到最底层(k<n);
若1可行,我们可以求出由最高层向下出发的最大电梯可行区间;
若2可行,我们可以求出由最底层向上出发的最大可行区间;
只要判断我们的电梯是否在可行区间内即可。
最后要说下注意特判!注意特判!注意特判!如果在a==b直接就是YES
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
ll A[5];
ll B[5];
int main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int T;cin>>T;
ll n,k,a,b;
string ans;
while(T--){
ll idx=0;//记录可行区域的个数
cin>>n>>k>>a>>b;
ll t1=a-k;//用此值判断电梯此时能否到达底层
ll t2=a+k;//t2用于判断电梯能否到达顶层
ans="NO";
int flag =1;
if(a==b)ans="YES";//特判a==b
if(t1>1||t2<n){
if(t1>1){//a能到底层
A[++idx]=1+k;//更新当前编号的可行区域
B[idx]=n+1;
A[++idx]=0;
B[idx]=n-k;
flag=0;//若已经操作计算过两个可行区域flag置0防止多次计算
}
if(t2<n&&flag==1){//a能到顶层
A[++idx]=0;//更新当前编号的可行区域
B[idx]=n-k;
A[++idx]=1+k;
B[idx]=n+1;
}
for(int i=1;i<=2;i++){//若在电梯可行区域内则为yes
if(b>A[i]&&b<B[i])ans="YES";
}
}
cout<<ans<<endl;
}
return 0;
}
D.最大 gcd
原题链接:登录—专业IT笔试面试备考平台_牛客网
思路:这题第一次没做过类似的题目,确实不好上手
1.遍历范围内的每个数,考虑每一个数是否能成为最大公约数。
2.二层遍历,查找第一层循环所数的倍数,如果存在第二层循环数是第第一层循环数的倍数,那么第一层循环的数一定是这两个数的最大公约数,枚举最大值就是答案;例:第一层循环i=3,第二层循环j=6。gcd(3,6)=3;
问题分析:
1.复杂的分析O(nlog(n))
第一层循环n,第二层循环是个调和级数
O( f( n ) )= n/2 + n/3 + n/4 + ... + n/n
Σ n/k = ln( n )
O(n)=nln(n)
数据范围为1e6可行
2.具体实现:遍历第二层循环时记录倍数的个数,若倍数个数>=2则可行(因为必定有一个倍数为自己,倍数个数>=2,说明有一对可以实现 gcd(i,j)=i 的数),与最大值比较即可。
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxl=1e6+10;
int A[maxl],idx;
int main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int n,a;cin>>n;
int maxA=0;
for(int i=1;i<=n;i++){
cin>>a;
maxA=max(maxA,a);
A[a]++;
}
int ans=1;
for(int i=2;i<=maxA;i++){
int cnt=0;
for(int j=i;j<=maxA;j+=i){
cnt+=A[j];
if(cnt>=2)break;
}
if(cnt>=2)ans=max(i,ans);
}
cout<<ans<<endl;
return 0;
}
E.一道难题
原题链接:登录—专业IT笔试面试备考平台_牛客网
思路:
1.看到只含有0,1 可以想到二进制优化。
2. 枚举所有范围内只含0 1的元素,判断所有元素的可行性。
3.每一个0 1组都表示一个二进制数,也表示一个十进制数。所以我们遍历在范围内的所有0 1组也可以简化为遍历对应0 1组二进制所构成的十进制数。遍历判断对应数是否有连续的三个1即可
例:n=2001 对应最大数为1111,化为2进制遍历7(111)-15(1111)即可。
4.此题数据范围较大10^23 所以用long long不可行,必须用string类型记录初始数据
代码分析:
1.数据范围1e23,若用二进制优化全为 1 时,数据范围变为2^23即8400000左右,复杂度可行。
2.找到所有给定n范围内只由0 1组成的元素;
3.为了枚举我们要找到最大的0 1组成元素,如2001最大即为1111,遍历n的每一位,若当前位大于1则更新前面所有待定项为1;
例:102001遍历到第四位前待定项为A[]={0,0,1},遍历完第四位后2>1加入1并更新之前所有元素,A[]={1,1,1,1};
#include<cstdio>
#include<algorithm>
#include<iostream>
using namespace std;
int A[25];
int main()
{
int idx=0;
string n;cin>>n;
for(int i=n.length()-1;i>=0;i--){//提取n的每一位元素
A[++idx]=n[i]-'0';
if(A[idx]>1){//若当前位大于1,更新之前所有元素为1(最大)
for(int j=1;j<=idx;j++)A[j]=1;
}
}
int n1=0;
for(int i=idx;i>=1;i--){//把数组A中元素映射到二进制,并将二进制换成n1
n1*=2;
n1+=A[i];
}
int ans=0;
int tmp,count1;
for(int i=7;i<=n1;i++){
count1=0;
tmp=i;
while(tmp){//判断这个数
if(tmp%2)count1++;
else count1=0;
tmp/=2;
if(count1==3){
ans++;
break;
}
}
}
cout<<ans<<endl;
return 0;
}
F.序列操作
原题链接:登录—专业IT笔试面试备考平台_牛客网
思路:数论题
贴大佬的思路s7win99的比赛主页
分析:
题中所描述的操作是将区间内的 a 变为 (a + x)%p 或 a%p
操作可执行多次 通式为 (ai + kx)%p k∈(0,1,2,3,4...);
k为当前a 加了多少个 x
可得同余方程:
(ai + kx) ≡ bi (mod p)
kx ≡ (bi − ai) (mod p)
得 k 的表达式:
k = ((bi − ai) ∗ x^(p − 2)) % p
( bi - ai ) 和 x的数据范围都在 p 内 所以我们枚举出所有 k 对应的解是可行的,因为题目描述的操作是将 al 变为 (al + x)%p 或 al%p,可以理解为在区间的数可以+x 可以不操作,那么枚举( bi - ai )得到 k 的最大值一定是,当前 x 所对应的操作数的最小值。
例:数组 a1、a2、a3、a4、a5,完成与 b 序列相等,分别所需的操作数是1,2,3,4,1
我们考虑最贪心的情况就是每次l ~ r都覆盖全局,我们最少操作四次就能完成目标,a1在四次操作中只加一次 x,a2只加两次 x,a3 只加三次 x,a4 只加四次 x,a5 只加一次 x。
枚举出每个 x 最小的操作数的最小值即可。
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxl=1e6+10;
int pown(int x,int y,int p)
{
int ans=1;
while(y){
if(y&1)ans=ans*x%p;
y/=2;
x=x*x%p;
}
return ans;
}
int a[maxl],b[maxl],d[maxl];
int main()
{
ios::sync_with_stdio(false);cin.tie(0);
int n,p;cin>>n>>p;
for(int i=1;i<=n;i++){
cin>>a[i];
a[i]%=p;
}
for(int i=1;i<=n;i++){
cin>>b[i];
if(a[i]!=b[i])
d[(b[i]+p-a[i])%p]=1;//记录每个差值(bi-ai)%p
}
int cnt=0;
for(int i=0;i<p;i++)
cnt+=d[i];
if(!cnt){//若bi - ai 都为 0 输出 0
cout<<cnt<<endl;
}
else{
int ans,kest=1e9;
for(int i=1;i<p;i++){//遍历 x
int tmp=pown(i,p-2,p),kt,km=0;
for(int j=1;j<p;j++){//遍历 d
if(d[j]){
kt=j*tmp%p;//当前k的值
km=max(km,kt);//遍历 bi - ai 找当前 i 中最大的 k
}
}
if(km<kest){//当 k 更小时 更新答案,因为是顺序从小到大遍历的,所以得到的ans一定是最小的
kest=km;
ans=i;
}
}
cout<<ans<<endl;
}
return 0;
}