牛客小白月赛57

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;
}

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值