新生赛002题解

题目链接
第一次出题好像出难了Ovo,其实大部分都是些思维题啊…
写的有点仓促,没加详细注释,不清楚的地方直接来问我吧

A.构造最长递增子串

题意:

给定一段序列,选其中连续的一段(ai,ai+1…,aj),最多更改其中的一个数为任意值,使其变为递增子串。求这样的递增子串的最大长度。

思路:

因为要找的序列元素是连续的,所以可以正序dp求出以ai 结尾的最长递增子串,倒序dp求出以ai 开头的最长递增子串。
然后只需要从头到尾扫一遍,更改当前ai的值判断能不能使ai两边的串连起来(不能则舍掉一边),更新最大长度即可。

#include<iostream>
#include<stdio.h>
#include<math.h>
#include<algorithm>
using namespace std;
const int N=100007;
int n,l[N],r[N],a[N],ans;
int main(){
	while(~scanf("%d",&n)){
		l[0]=r[0]=l[n+1]=r[n+1]=a[0]=a[n+1]=ans=0;
		for(int i=1;i<=n;i++)scanf("%d",&a[i]);
		for(int i=1;i<=n;i++)
			if(a[i]>a[i-1])l[i]=l[i-1]+1;
			else l[i]=1;
		for(int i=n;i;i--)
			if(a[i]<a[i+1])r[i]=r[i+1]+1;
			else r[i]=1; 
		for(int i=1;i<=n;i++)
			if(a[i+1]-a[i-1]>1)
				ans=max(ans,r[i+1]+l[i-1]+1);
			else 
				ans=max(ans,max(l[i-1]+1,r[i+1]+1));
		printf("%d\n",ans);
	}
}

B.Is it beautiful?

题意:

给定一个[1…n]的排列,对于前k个数,判断是否有一个区间[ai,ai+1,…,aj]使这个区间内恰好只有前k个数。(k依次从[1…n]取值)

思路:

因为如果存在这样的区间[l,l+1,…,r]使区间内恰好只有前k个数,则区间长度为r-l+1,所以可以在输入时预处理排列中[1…n]每个数的位置,对数值进行排序。
开两个变量记录包含当前值k(并包含之前值)需要的最大左右端点l和r,判断r-l+1是否等于k即可。

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=200007;
int t,n,p[maxn],ans[maxn];
 
int main(){
	//freopen("now.in","r",stdin);
	cin>>t;
	int l,r;
	while(t--){
		scanf("%d",&n);
		l=maxn;r=0;
		int x;
		for(int i=1;i<=n;i++){
			scanf("%d",&x);
			p[x]=i;
		}
		for(int i=1;i<=n;i++){
			l=min(l,p[i]);
			r=max(r,p[i]);
			if(r-l+1==i)ans[i]=1;else ans[i]=0;
		}
		for(int i=1;i<=n;i++)printf("%d",ans[i]);
		printf("\n");
	}
//	fclose(stdin);
}

C.Juicer

题意:

类似于用参差不齐的木板造的水桶接水,能接的最大水平面等于最短的木板高度,这个题是有一个表面参差不齐的矩形碗,给了每个位置的高度,求这个碗最多能接多少水。

思路:

假设有这样一个碗。首先这个碗的最外面一圈是不能接水的,因为会溢出取
2 3 4
5 1 6
7 8 9
那么中间的1可以填到和四周最低那个高度,就是3(因为只能向上下左右溢水,所以不是2)
所以想到可以用堆预处理每个点的高度,然后bfs,每次向上下左右扩展,并求当前点可以接水的最大高度。

#include<iostream>
#include<queue>
#include<cstdio>
using namespace std;
typedef long long ll;
const int maxn=307;
int w,h,a[maxn][maxn],f[maxn][maxn];
ll ans;
const int x_[4]={1,0,-1,0};
const int y_[4]={0,1,0,-1};
struct node{
	int x,y,v;
	friend bool operator <(node a,node b){
		return a.v>b.v;
	}
}t;
priority_queue<node>q;

int main(){
	//freopen("now.in","r",stdin);
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	cin>>w>>h;
	for(int i=1;i<=h;i++)
	for(int j=1;j<=w;j++)cin>>a[i][j];
	for(int i=1;i<=w;i++){
		f[1][i]=1;t.x=1;t.y=i;t.v=a[1][i];q.push(t);//up
		f[h][i]=1;t.x=h;t.y=i;t.v=a[h][i];q.push(t);//down
	}
	for(int i=2;i<h;i++){
		f[i][1]=1;t.x=i;t.y=1;t.v=a[i][1];q.push(t);//left
		f[i][w]=1;t.x=i;t.y=w;t.v=a[i][w];q.push(t);//right
	}
	while(!q.empty()){
		t=q.top();q.pop();
		for(int i=0;i<=3;i++){
			int xt=t.x+x_[i],yt=t.y+y_[i];
			if(xt<1||xt>h||yt<1||yt>w||f[xt][yt])continue;
			f[xt][yt]=1;
			if(a[xt][yt]<t.v){
				ans+=t.v-a[xt][yt];
				a[xt][yt]=t.v;
			}
			node p={xt,yt,a[xt][yt]};
			q.push(p);
		}
	}
	cout<<ans;
	//fclose(stdin);
}

D.Eat Candies

题意:

有三堆糖果,每天吃两块不同的糖果(必须是不同的两堆),求能吃的最多天数。

思路:

数学思维题。可能有多种方法。
其中一种方法是先把三堆糖果从小到大排序。
1.先一直吃第一堆and第三堆,直到第三堆数量等于第二堆或者第一堆已吃完。
2.如果第一堆仍有剩余,将其均分为两份,第一份与第二堆同时吃,第二份与第三堆同时吃
3.最后同时吃第二堆and第三堆

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+7;
int t,a[4];

int main(){
	cin>>t;
	while(t--){
		int ans=0;
		cin>>a[1]>>a[2]>>a[3];
		sort(a+1,a+1+3);
		int k=a[3]-a[2];
		if(a[1]>=k)ans+=k,a[3]-=k,a[1]-=k;
		else ans+=a[1],a[3]-=a[1],a[1]=0;
		if(a[1]){
			k=a[1]/2;
			a[2]-=k;
			a[3]-=k;
			ans+=2*k;
		}
		ans+=a[2];
		cout<<ans<<endl;
	}
}

E.由你来决定怎么颁奖

题意:

在满足所有奖牌数量条件下任意决定金银铜牌数量

思路:

贪心。先从大到小排序
金牌数严格小于铜牌和银牌数,所以让金牌成绩线只等于最高成绩,使获得金牌的数量最小。
银牌处理方式同类似于金牌,但要同时满足数量比金牌多。
剩下能颁发的奖都给铜牌。判断金牌数量是否大于银牌且大于铜牌数量。

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=400007;
int t,a[maxn],n;
int cmp(int x,int y){return x>y;}
int main(){
	//freopen("now.in","r",stdin);
	cin>>t;
	while(t--){
		scanf("%d",&n);int k=n/2;
		for(int i=1;i<=n;i++)scanf("%d",&a[i]);
		sort(a+1,a+1+n,cmp);
		int x=0,y=0,z=0;
		int i=1;
		while(a[i]==a[1]&&i<=k)x++,i++;
		int t=a[i];
		while(a[i]==t&&i<=k)y++,i++;
		while(y<=x&&i<=k){
			t=a[i];
			while(a[i]==t&&i<=k)y++,i++;
		}
		while(a[i]!=a[k+1]&&i<=k)z++,i++;
		if(x>=y||x>=z){
			printf("0 0 0\n");
			continue;
		}
		printf("%d %d %d\n",x,y,z);
	}
	//fclose(stdin);
}

F.XorXor

题意:

设f(i,j)=ai^ ai+1^ ai+2^ …^aj 。
求[f(1,1) ^ f(1,2) f(1,n) ] ^ [f(2,2) ^ f(2,3) f(2,n)] ^ [f(n,n)]

思路:

首先要知道一件事情,异或的逆运算是异或
所以假设k1=[f(1,1) ^ f(1,2) f(1,n) ],其中a[1]是被异或了(n-1+1)次
那么k2=[f(2,2) ^ f(2,3) f(2,n)] ,只需要把k1异或的(n-1+1)次a[1]异或掉就是k2。
同理k3=k2异或(n-2+1)次a[2],k4=k3异或(n-3+1)次a[3] …
最终答案就是k1到kn的异或和。
所以只需要从头到尾扫一遍即可出结果。
这里还要知道一件事可以使上述过程简化,任何数异或它本身=0,任何数异或0是它本身,所以一个数自身异或奇数次是它本身,自身异或偶数次是0。

#include<iostream>
#include<cstdio>
using namespace std;
const int N=100007;
int a[N],ans,now,n,t;

int main(){
	cin>>t;
	while(t--){
		cin>>n;
		now=ans=0;
		for(int i=1;i<=n;i++){
			scanf("%d",&a[i]);
			if((n-i+1)%2)now^=a[i];
		}
		for(int i=1;i<=n;i++){
			ans^=now;
			if((n-i+1)%2)now^=a[i];
		}
		cout<<ans<<endl;
	}
}

G.0011

题意:

原始串“01”,可以在任意位置添加任意个“01”。
给n个串,问每个串是否能由上述方法得来。

思路:

假设从头到尾扫一遍当前串,如果扫到0,那么后面必定要有与之对应的1,如果扫到1,前面必须要由一直对应的0。
所以可以设一个量now,从头到尾扫,扫到0就now++,扫到1就now–。
1.如果中途now<0,则说明当前扫到的1前面没有与之对应的0,错误。
2.如果最终扫完之后now!=0,则说明不完全对应,有多余的0或者1,错误。
如果未发生上述两种情况,便是正确的串。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<string.h>
using namespace std;
int n,now;
char s[1007];
int main(){
	cin>>n;
	while(n--){
		cin>>s;
		int f=1;
		for(int i=0;i<strlen(s);i++){
			if(s[i]=='0')now++;
			if(s[i]=='1')now--;
			if(now<0)f=0;
		}
		if(now)f=0;
		if(!f)cout<<"NO\n";else cout<<"YES\n";
	}
}

H.Perfect String

题意:

给一个仅由a,b,c,?构成的字符串,要求把所有问号变成a/b/c,使任意相邻的两个字符不相同。

思路:

(我原本以为前面变的字符会对后面产生影响,结果写了特别长的代码判断了各种情况才把这道题AC,后来经梁哥讲解,改前面的字符不会对后面的产生什么影响,因为任意两个字符中间的问号都有方法改成两边的字符不一样)


所以就是这个思路,从头到尾扫,碰到 ? ,只需要把它变成跟前后两个字符不一样的字符即可。

#include<iostream>
#include<string.h>
#include<cstdio>
using namespace std;
int t,f;
string s;
int main(){
	//freopen("now.in","r",stdin);
	cin>>t;
	while(t--){
		cin>>s;
		f=1;
		if(s[0]=='?'){
			if(s[1]!='a')s[0]='a';else
			if(s[1]!='b')s[0]='b';else
			if(s[1]!='c')s[0]='c';
		}
		for(int i=0;i<s.length();i++){
			if(s[i]=='?'){
				if(s[i+1]!='a'&&s[i-1]!='a')s[i]='a';else
				if(s[i+1]!='b'&&s[i-1]!='b')s[i]='b';else
				if(s[i+1]!='c'&&s[i-1]!='c')s[i]='c';
			}else
			if(s[i]==s[i+1]){f=0;break;}
		}
		if(f)cout<<s<<"\n";else printf("-1\n");
	}
	//fclose(stdin);
}

I.十进制中的二进制

题意:

找小于n的所有数中仅由0和1构成的数的数量。

思路:

dfs,用0和1填10位数,造出来的数小于n结果就++

#include<iostream>
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
int n,ans;
void dfs(int v,int d){//d是位数,为了方便从高位到低位搜索
	if(!d){
		if(v<=n&&v)ans++;
		return;
	}
	dfs(v*10,d-1);
	dfs(v*10+1,d-1);
}
int main(){
	while(~scanf("%d",&n)){
		ans=0;
		dfs(0,10);
		printf("%d\n",ans);
	}
}

J.新年快乐

题意:

给梯形四个点坐标,求梯形的面积。

思路:

从四个坐标给出的形式可以看出这是个直角梯形,而且直角边在x轴上。
所以很简单就能签到成功了。S=1/2(上底+下底)*H

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
int x,y,z,t;
int main(){
	while(~scanf("%d%d%d%d",&x,&y,&z,&t)){
		double ans=0.5*(z-x)*(y+t);
		printf("%.1lf\n",ans);
	}
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

linkscx

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值