训练赛2题解

A构造最长递增子序

题意

给定一个数组,选其中连续的一段(ai,ai+1…,aj),最多只能改变段中的任意一值使这一段能变成递增的序列,在所给数组里这个序列的最大长度

思路

先正序遍历一下数组记录每个位置最长递增序列的长度放到一个数组里,意思就是1 2 3 4 2 4 5 6 则第一个位置的最长递增序列的长度就是1 因为就它一个,2的就是2 (12)3就是3(123)4的是4(1234)5的是1(4>2,只剩2这一个)…依次类推,然后再倒着遍历一下这个数组,同理记录每个位置最长递减序列的长度(倒着就是递增),记录完了之后,遍历每个位置求最大值,方法是看第i位置他的后一个数时候比前一个数大(并且至少得大2)如果大的话只需要改动第i个位置就可以使前后连起来了,长度就是i-1递增序列的长度加上i+1递增序列的长度再加1(还得加上第i个数),如果不满足i+1比i-1至少大二,说明连不起来,那么再从i-1递增序列加1和i+1递增序列里加1求最大值,加一是因为第i个数是可以改的。详细还有注释

代码

#include<iostream>
#include<algorithm>
#include<cstring> 
using namespace std;
const int N=100010;
int a[N],l[N],r[N];
int ans;
int main()
{
	ios::sync_with_stdio(false);//不加cin会超时 
	int n;
	while(cin>>n)
	{
		ans=0;
		a[0]=a[n+1]=0;
		r[0]=r[n+1]=l[0]=l[n+1]=0;
		for(int i=1;i<=n;i++)
		 cin>>a[i];
		for(int i=1;i<=n;i++)
		{
		 if(a[i]>a[i-1])
		  l[i]=l[i-1]+1;//就是从前往后遍历,每个l数组对应的是相应下标的最长递增数列的长度 
		 else
		  l[i]=1;//如果不递增了就应该改成1重新来啊
	    }
	    for(int i=n;i>=1;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,l[i-1]+r[i+1]+1);//这是两个递增序列能连起来的情况就前后两个长度加起来
			 //再加上中间的可以改的 
	    	else
	    	 ans=max(ans,max(l[i-1]+1,r[i+1]+1));//这是连不起来的情况,所以就单独求前后哪个最大,
			 //加1是因为还可以改前面或后面的一个数 
	    }
	    cout<<ans<<endl;
		 
	}
	return 0;
}

B. Is it beautiful?

题意

给定一个数组长度为n(这个数组只能由1到n组成),然后从这个数组里取一段区间长度为k(k=123…n),然后这个长度为k的区间里可以正好包含前k个数(排好序的前k个数)如果可以相应的长度就输出1不可以相应的长度就输出0

思路

思路其实挺简单的就是记录每个数的位置如果然后1到2到3一直到这个数如果位置是连续的那个这个数就是可以的。判断位置是否连续的方法是:因为如果连续从小到大每个数之间也都差1,那么最大减去最小加一等于区间的长度即可。记录每个数的位置主要是因为如果符合要求那么最大值一定是这个数而且这个区间的数都是123一直到这个最大数就很巧妙地与数组下标结合了起来,详细可看注释

代码

#include<iostream> 
#include<algorithm>
using namespace std;
const int N=200010;
int a[N],b[N];
int main()
{
	ios::sync_with_stdio(false);
	int t;
	int maxn,minn;
	int n;
	cin>>t;
	while(t--)
	{
		cin>>n;
		minn=N;
		maxn=0;
		int x;
		for(int i=1;i<=n;i++)
		{
			cin>>x;
			a[x]=i;//记录数组的位置如输入451326则变为354126即1在原数列的位置是3,2位置是5...
	    }
	    for(int i=1;i<=n;i++)
	    {
	    	minn=min(minn,a[i]);
	    	maxn=max(maxn,a[i]);
	    	if(i==maxn-minn+1)
	    	 b[i]=1;
	    	else
	    	 b[i]=0;
	    }
	    for(int i=1;i<=n;i++)
	     {
	     	if(i==n)
	     	 cout<<b[i]<<endl;
	     	else
	     	 cout<<b[i];
	     }
	}
	return 0;
}

c.juicer

题意

这里是引用

思路

这里是引用

代码

在这里插入代码片

g.0011

题意

就是给定一个字符串判断是否满足以下条件:就是完全是由01组成而且从任意位置插入01,如果先给了01 可以从后面插入变为0101 也可中间插入0011也可前面0101,然后可以从得到的字符串里继续插01以此类推得到的字符串,判断所给的字符串是不是这样的

思路

看了大佬的做法真的太厉害了,因为是插入的是01所以不管怎么插入,1之前总有与他对应的0也就是0 1个必须数相同,而且0都是出现在1前面的我们就可以假设一个数为0然后读这个字符串如果读到0加1读到1减一,很容易知道如果满足条件在读的过程中这个数是一直大于等于0的如果他小于0 了就说明在0前面出现了与它对应的1就不符合要求了,,别忘了最后判断这个数最后一定是0的 不是0其实也不符合要求

代码

#include<iostream>
#include<cstring>
#include<string> 
using namespace std;
int main()
{
	char s[1010];
	int f,n,now;
    cin>>n; 
	while(n--)
	{
		now=0;
		cin>>s;
		f=1;
		for(int i=0;i<strlen(s);i++)
		{
			if(s[i]-'0'==0)now++;
			if(s[i]-'0'==1)now--;
			if(now<0)
			{
				f=0;
				break;
			}
		}
		if(now)
		 f=0;//now最后必须是0因为01的个数是相等的 
		if(f)
		 cout<<"YES"<<endl;
		else
		 cout<<"NO"<<endl;
	}
	return 0;
}

i.十进制中的二进制

题意

就是给你一个数看小于等于这个数且只有01(可全部由0或1也可以由01混合组成)组成的数(不包括0)总共就多少个

思路

这题可以和就是列举位数可以发现小于某位数的这类数的个数的有规律的,比如说小于两位数的这种数也就是小于10的有1个,小于100的有3个,小于1000的有7个可以得出规律dp[i]=dp[i-1]+2^(i-1),i表示的位数,dpi的含义是位数小于i+1位的所有数不含i位,然后处理完了知道小于某位的所有这种数的个数了,我们就可以考虑把已经知道的数先求出他的位数,在求出他每一位是什么数放到数组里,如果他是个n位数 且第n位大于等于2,那么他肯定包含了所有小于n+1位数的情况,所以直接加上dpi退出就行了,如果他是1那么他只是包含了所有的与自己相同位数的,也就是dpi-1,在加上1因为他是1所以得加他自身(这点注释里有说明的)说的有点乱不过自己能看懂,就看个例子吧 比如说这是个四位数 ,假设是2xxx 那么他肯定包含了 所有小于 10000的这类数 所以个数就是dpi(表示小于10的i次方的总数)而且后面的位数就不用看了,如果他是1xxx 那么他保底是1000吧 ,他是不是包含了 所有小于1000的 就是 加上dp[i-1](不包含1000)所以加上1000就是加1,然后再后面的每一位都这么处理。

代码

#include<iostream>
#include<cstdio>
using namespace std;
typedef long long ll;
int main()
{
	ll dp[11];
	ll weishu[11];
	dp[1]=1;
	dp[0]=0;
	ll now=2;
	for(int i=2;i<11;i++)     
	  {
	   dp[i]=dp[i-1]+now;
	   now*=2;
      }//这里生求小于10的i次方的符合要求的数比如dp2就是小于100的个数 就是三 这样把每一位的都求出
	ll n;
	while(cin>>n)
	{
		ll sum=0;
		ll ans=0,p=1;
		ll temp=n;
		while(temp>0)
		{
			temp/=10;
			ans++;
			p*=10;//100  1  10     10 2 100  1  3 1000 0 4 100000 
		}
		p/=10;
		temp=n;
		ll t=ans;
		for(int i=1;i<=ans;i++)
		{
			weishu[i]=temp/p;
			temp%=p;
			p/=10;
		}
		for(int i=1;i<=ans;i++)
		{
			if(weishu[i]>=2)
			{
				sum+=dp[t];
				break;
			}
			if(weishu[i]==1)
			{
				sum+=dp[t-1]+1;//加一是因为多了个
				//这么说吧如果是1他肯定大于比他小一位数的所有数
				 //就包含了小于他一位的那些数,加一是因为多一位凑成0
				 //也就是它本身 
			}
			t--;//位数减一 
		}
		cout<<sum<<endl;
	} 
	return 0;
} 

j.新年快乐

题意

给出四个点的坐标(x, 0), (x, y), (z, 0), (z, t)求所围成图形的面积

思路

如果没有点重合所为成的就是梯形 而且是直角梯形 所为成的面积是上底加下底乘高除二就是(y+t)*(z-x))/2.0

代码

#include<iostream>
#include<iomanip>
using namespace std;
int main()
{
	double x,y,z,t,a;
	

    while(cin>>x>>y>>z>>t)
   {
		
	
	a=((y+t)*(z-x))/2.0;
	cout<<fixed<<setprecision(1)<<a<<endl;
   }
 return 0;
}

发布了8 篇原创文章 · 获赞 0 · 访问量 118

猜你喜欢

转载自blog.csdn.net/xgx984826498/article/details/103821019