美团2023届笔试题解

(4/4)欢乐AK

以下所有代码加上本人前面的头文件(如下)就可以跑啦~

#pragma GCC optimize("O3")
#pragma GCC target("avx2")
#include
#define ll long long
#define maxn 1000005
#define inf 1e9
#define ins insert
#define pb push_back
#define vi vector <int>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
using namespace std;

inline ll read()
{
	ll x=0,w=1; char c=getchar();
	while(c<'0'||c>'9') {if(c=='-') w=-1; c=getchar();}
	while(c<='9'&&c>='0') {x=(x<<1)+(x<<3)+c-'0'; c=getchar();}
	return w==1?x:-x;
}

T1

题目大意:给出n个数,让你选出一个最大的子集,使得任意两个数的差>1
n < = 1 e 5 , a [ i ] < = 2 e 5 n<=1e5,a[i]<=2e5 n<=1e5,a[i]<=2e5

显然直接用 d p [ i ] dp[i] dp[i]表示现在最大的数选到 i i i,最大的子集是多少,然后直接转移即可。

代码如下:

int dp[maxn],n,a[maxn],v[maxn];

int main()
{
	n=read(); rep(i,1,n) a[i]=read(),v[a[i]+1]=1;
	rep(i,2,200001) dp[i]=max(dp[i-1],dp[i-2]+v[i]);
	cout<<dp[200001]<<endl;
	return 0;
}

T2

题目大意:给出长度为n的数组,可以翻转任意一段,问翻转一段之后的数组最大子段和为多少
n < = 1 e 5 , − 1 e 3 < = a [ i ] < = 1 e 3 n<=1e5,-1e3<=a[i]<=1e3 n<=1e5,1e3<=a[i]<=1e3

可以发现:翻转操作只是相当于,我们可以选择两个子段进行统计答案。
所以我们从前往后做一遍存到 d p 1 dp1 dp1,从后往前做一遍存存到 d p 2 dp2 dp2,分别表示,从该位开始的最大子段和为多少。
然后我们记录一下 d p 1 dp1 dp1数组的前缀 m a x max max,就可以直接统计答案了

代码如下:

ll n,a[maxn],dp1[maxn],dp2[maxn],ans,pmx[maxn];

int main()
{
	n=read(); rep(i,1,n) a[i]=read();
	rep(i,1,n) dp1[i]=max(0ll,dp1[i-1])+a[i],pmx[i]=max(pmx[i-1],dp1[i]);
	per(i,n,1) dp2[i]=max(dp2[i+1],0ll)+a[i];
	rep(i,1,n) ans=max(ans,pmx[i]+dp2[i+1]);
	cout<<ans<<endl;
	return 0;
}

T3

题目大意:有一个 n ∗ n ∗ n n*n*n nnn大小的立方体,进行m次操作,每次操作选择从x或y或z的一个面进行切割(给定操作,例如给x=4就是沿着x=4的面进行切割),问m次操作时,每次操作后最大体积的立方块大小。
n , m < = 1000 n,m<=1000 n,m<=1000

可以发现,xyz三个维度是独立的,我们每次操作就相当于,在xyz三个线段中选择一个线段从中间断开。
那么我们维护一下这个过程,每次统计答案就是最大的 x x x*最大的 y y y*最大的 z z z了。
本人直接使用了set维护,简单暴力,当然应该有更好的维护方法。

代码如下:

int n,m,a[maxn],A[4];
char opt[maxn];
struct node{int l,r;};

inline bool operator < (node a,node b)
{
	if(a.l!=b.l) return a.l<b.l;
	return a.r<b.r;
}

multiset <node> sx[4];
multiset <node>::iterator it;

int main()
{
	n=read(); m=read(); rep(i,1,m) cin>>opt[i]; rep(i,1,m) a[i]=read();
	rep(i,0,2) sx[i].ins({0,n}),A[i]=n;
	rep(i,1,m)
	{
		int id;
		if(opt[i]=='x') id=0;
		else if(opt[i]=='y') id=1;
		else id=2;
		
		for(it=sx[id].begin();it!=sx[id].end();it++)
		{
			node tmp=*it;
			if(a[i]>=tmp.l&&a[i]<=tmp.r)
			{
				sx[id].ins({tmp.l,a[i]});
				sx[id].ins({a[i],tmp.r});
				sx[id].erase(it); break;
			}
		}
		A[id]=0;
		for(it=sx[id].begin();it!=sx[id].end();it++)
		{
			node tmp=*it; //cout<
			A[id]=max(A[id],tmp.r-tmp.l);
		}
		cout<<A[0]*A[1]*A[2]<<endl;
	}
	return 0;
}

T4

题目大意:给你q次区间加,区间求和的操作,需要你重新排列一下原数组,使得每次查询区间和的答之和最大。
n < = 1000 , q < = 500 n<=1000,q<=500 n<=1000,q<=500

发现只需要统计一下每个位置的数对答案的贡献就ok了。
所以我们每次区间加,就是区间内每个点的贡献+1,然后区间加操作照常就好。
然后根据贪心思想,贡献越多的地方,应该放的数就越大,排个序统计贡献就做完了。

然后由于这个数据范围很小,并不需要真的写一个线段树,暴力处理就ok了。

代码如下:

ll n,Q,a[maxn],cnt[maxn],ans,nw[maxn];

int main()
{
	n=read(); Q=read(); rep(i,1,n) a[i]=read();
	while(Q--)
	{
		int opt=read();
		if(opt==1)
		{
			int l=read(),r=read();
			rep(i,l,r) ans+=nw[i],cnt[i]++;
		}
		else
		{
			int l=read(),r=read(),w=read();
			rep(i,l,r) nw[i]+=w;
		}
	}
	sort(cnt+1,cnt+n+1); sort(a+1,a+n+1);
	rep(i,1,n) ans+=a[i]*cnt[i];
	cout<<ans<<endl;
	return 0;
}

END

你可能感兴趣的:(笔试,简单,算法,c语言,开发语言)