AcWing 第16周 周赛

题目1:3955. 统一大小写 - AcWing题库

题目描述:

给定一个单词,单词中既可能包含大写字母也可能包含小写字母。

现在,要将单词进行统一大小写的操作,具体操作规则为:

  • 如果一个单词中包含的大写字母数量大于小写字母数量,则将所有字母统一为大写,例如 ViP 应改为 VIP
  • 如果一个单词中包含的大写字母数量小于小写字母数量,则将所有字母统一为小写,例如 HoUse 应改为 house
  • 如果一个单词中包含的大写字母数量等于小写字母数量,则将所有字母统一为小写,例如 maTRIx 应改为 matrix

输入格式

第一行包含整数 T,表示共有 T 组测试数据。

每组数据占一行,包含一个由大小写字母构成的字符串 s。(注意,字符串可能只包含小写字母或只包含大写字母)。

输出格式

每组数据输出一行结果,一个字符串,表示统一大小写后的单词。

数据范围

本题共两个测试点。
小测试点,如样例所示。
大测试点满足:1≤T≤30 ,字符串长度取值范围 [1,100]

输入样例:

3
HoUse
ViP
maTRIx

输出样例:

house
VIP
matrix

解题思路及过程

周赛时现场写的代码(Accepted):由于这里 strlwr()strupr() 函数用不了,所以就遍历整个字符串数组改变大小写。

#include<iostream>
#include<cstring>
using namespace std;

int main()
{
	char a[101];
	int i,c,d,T,j;
	cin>>T;
	
    while(T--)
	{
    c=0;d=0; 
	cin>>a;
	
    for(i=0;i<strlen(a);i++)
	{
    if('A'<=a[i] && a[i]<='Z')
	    c++; // 统计大写字母 
	if('a'<=a[i] && a[i]<='z')
		d++; // 统计小写字母 
	}
    
    if(c>d)
    {
    for(int m=0;m<strlen(a);m++)
	{
	if('a'<=a[m] && a[m]<='z')
	    cout<<char(a[m]-32);
    else
	    cout<<a[m];
    }
	cout<<endl;
    }
    
    if(c<d || c==d)
    {
	for(int n=0;n<strlen(a);n++)
	{
	if('A'<=a[n] && a[n]<='Z')
	    cout<<char(a[n]+32);
	else
	    cout<<a[n];
    }
	cout<<endl;
    }

    }
    return 0;
}

完整AC code

#include<iostream>
#include<ctring>
#include<algorithm>
using namespace std;

int main()
{
	int T;
	cin>>T;
	while(T--)
	{
		string str;
		cin>>str;
		int a=0,b=0;
		for(auto c:str)
		if(c>='a')
		    a++;
		else
		    b++;
		
		if(b>a)
		{
			for(auto& c:str)
			c=toupper(c);
		}
		else
		{
			for(auto& c:str)
			c=tolower(c);
		}
		cout<<str<<endl;
	}
	return 0;
}

题目2:3956. 截断数组 - AcWing题库

题目描述:

给定一个长度为 n 的数组 a1,a2,…,an。

现在,要将该数组从中间截断,得到三个非空子数组。

要求,三个子数组内各元素之和都相等。

请问,共有多少种不同的截断方法?

输入格式

第一行包含整数 n。

第二行包含 n 个整数 a1,a2,…,an。

输出格式

输出一个整数,表示截断方法数量。

数据范围

前六个测试点满足 1 ≤ n ≤ 10
所有测试点满足 1 ≤ n ≤ 10^5−10000 ≤ ai ≤ 10000

输入样例1:

4
1 2 3 3

输出样例1:

1

输入样例2:

5
1 2 3 4 5

输出样例2:

0

输入样例3:

2
0 0

输出样例3:

0

 解题思路及过程

此题在周赛时间快结束的时候,有了思路,然后周赛结束后按照我的思路试着写了下。题目大概意思就是说将一个数组分为三等份使得每一部分数组元素之和相等。既然要分成三等份,前提就是要保证整个数组元素之和是3的倍数且数组元素个数要≥3。若不是3的倍数直接输出0。若能分成三等份,则进一步判断。一开始我的想法是,可以把数组分为两种,第一种就是数组元素全为0,所有划分可能的结果共有 (n-1)*(n-2)/2 种;第二种是数组元素不全为0。此时将数组元素从数组第一个元素开始,往后累加,当累加到 sum/3 时,做个记号,然后重新开始累加,同样一直累加到 sum/3 时做个记号,退出循环。但经过代码调试,发现这样写有漏洞。例如,当在数组中第一个划分点的后一个元素为0时,此时就多了一种划分方法,所以要对代码进行改动。将代码加上统计当正好在划分点的后面有几个0,此时记为c1;同理,统计第二个划分点的后面出现了几个0,记为c2。此时截断方法个数为(c1+1)*(c2+1)/2。然后调试了一下,发现还有样例通过不了。例如:{-5,-2,1,1,5,0,0,0},即再第一次划分点的后面一直到最后一个元素都是0这种情况,设数组中元素为0的个数为c,此时截断方法个数为 c*(c-1)/2 种。还有一种情况,例如:{-5,-2,1,1,5,0,-4,4,0,0,0},这种情况则是再第二次划分点的后面一直到最后一个元素全为0,此时截断方法个数应为 (c+1)*c/2 种。这样就又多了两种情况出来。这样感觉代码考虑地就比较全面了,然后就去OJ提交了一下代码,然后出现了 Wrong Answer ,错误示例如下所示:

输入:

100
3 0 -5 2 -3 -1 -1 0 -2 -5 -4 2 1 2 -2 -1 -1 -4 3 -1 -3 -1 5 0 -4 -4 -1 0 -2 -2 0 1 -1 -2 -1 -5 -4 -2 3 1 -3 0 -1 1 0 -1 2 0 -2 -1 -3 1 -2 2 3 2 -3 -5 2 2 -2 -2 1 2 -2 -1 3 0 -4 7 -2 2 1 4 -9 -1 -2 -1 0 -1 0 -2 -2 -1 1 1 -4 2 -3 -3 7 1 1 -3 -7 0 -2 0 5 -2

输出:

2

标准答案:

5

那么,到底哪一步出现了问题或者少考虑了哪种情况?这时我突然想到一组数列:{1,-1,1,-1,1,-1,1,-1},如果按照此前那种思路分析,第一个划分点在下标1和2之间,c1=0;第二个划分点在下标3和4之间,c2=0。那么截断方法为 (c1+1)*(c2+1)=1 种。不难发现,此数列在下标5和6之间也有可以有一个划分点,那么此时截断方法应共有 (3*2)/2=3 种。因此,这种方法行不通。下面附上我的 Wrong Answer 错误代码:

#include<iostream>
using namespace std;

int main()
{
	int n,i,j;
	cin>>n;
	int *p=new int[n],sum=0,c=0,c1=0,c2=0,m,l;
	p[n]=-1;
	for(i=0;i<n;i++)
	{
		cin>>p[i];
		sum+=p[i];
		if(p[i]==0)
		c++; // 统计输入数组元素中0的个数为c
	}

	if(c!=n)  // 数组元素不全为0
	{

	    if(sum%3!=0 || n<3)
	    cout<<0<<endl;
	    if(sum%3==0 && n>=3)
	    {
	        int avg=sum/3; // 每个部分的和 
	        int sum1=0,sum2=0;
	        for(j=0;j<n-2;j++)
	        {
		    sum1+=p[j];
		    if(sum1==avg)
		    break;
	        }

	        if(j==n-2)
	        {cout<<0<<endl;return 0;}
	        else // 开始第1次划分 
	        {
		        int j1=j; 
		        while(p[j+1]==0)
		        {
		        c1++; // 统计第二部分之间连续0的个数
	            j++; 
		        }
		        if(j==n-1)
		        {
		        cout<<(c1)*(c1-1)/2<<endl;
		        }
		        else // 开始第2次划分 
		        {
		
	                for(j=j+1;j<n-1;j++)
     	            {
		            sum2+=p[j];
		            if(sum2==avg)
		            break;
		            }

	                if(j==n-1)
	                {cout<<0<<endl;return 0;}
	                else
	                {
	                    int j2=j;
	    	            while(p[j+1]==0)
	    	            {
		                c2++; // 统计第三部分之间连续0的个数 
		                j++;
			            }
		
			            if(j==n-1)
			            {
                        cout<<(c+1)*c/2<<endl;
			            }
    
                        else
	                    {
			            cout<<(c1+1)*(c2+1)<<endl;
	                    }
                    }
	            }
            }
        }
        else // 数组元素全为0的情况 
        {
    	cout<<(c-1)*(c-2)/2<<endl;	
	    }
        return 0;
}

下面附上该题完整AC code:

#include<iostream>
using namespace std;

typedef long long LL;
const int N = 100010;

int n;
int s[N];

int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>s[i];
		s[i]+=s[i-1];
	}
	
	if(s[n]%3)
	{
		puts("0");
		return 0;
	}
	
	LL res=0;
	for(int i=3,cnt=0;i<=n;i++)
	{
		if(s[i-2]==s[n]/3)
		cnt++;
		if((s[n]-s[i-1])==s[n]/3)
		res+=cnt;
	}
	cout<<res<<endl;
	return 0;
}

题目3:3957. 子序列 - AcWing题库

题目描述:

我们称一个序列是有序的,如果该序列是非严格单调递增序列或非严格单调递减序列。

现在,给定一个 n 个整数的序列 a1,a2,…,an。

请你找到该序列的最短非有序子序列。

注意,子序列不一定连续。

输入格式

第一行包含整数 n。

第二行包含 n 个整数 a1,a2,…,an。

输出格式

如果不存在非有序子序列,则输出一行 0。

否则,首先在第一行输出一个整数,表示最短非有序子序列的长度,随后在第二行按顺序输出该子序列的各元素下标。

如果方案不唯一,输出任意合理方案均可。

数据范围

前 6 个测试点满足 1 ≤ n ≤ 10
所有测试点满足 1 ≤ n ≤ 10^5−10^6 ≤ ai ≤ 10^6

输入样例1:

5
67 499 600 42 23

输出样例1:

3
1 3 5

输入样例2:

3
1 2 3

输出样例2:

0

输入样例3:

3
2 3 1

输出样例3:

3
1 2 3

 解题思路及过程

这题太难了,直接看代码吧。

AC code:

#include<iostream>
using namespace std;

const int N=100010;

int n;
int w[N];
int lmin[N],lmax[N],rmin[N],rmax[N];

int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
	    cin>>w[i];
	lmin[1]=lmax[1]=1;
	for(int i=2;i<=n;i++)
	{
		lmin[i]=lmax[i]=i;
		if(w[lmin[i-1]]<w[i])
		    lmin[i]=lmin[i-1];
		if(w[lmax[i-1]]>w[i])
		    lmax[i]=lmax[i-1];
	}
	
	rmin[n]=rmax[n]=n;
	for(int i=n-1;i;i--)
	{
		rmin[i]=rmax[i]=i;
		if(w[rmin[i+1]]<w[i])
		    rmin[i]=rmin[i+1];
		if(w[rmin[i+1]]>w[i])
		    rmax[i]=rmax[i+1];
	}
	
	for(int i=2;i<n;i++)
	{
		if(w[lmax[i-1]]>w[i] && w[rmax[i+1]]>w[i])
		{
			cout<<3<<endl;
			cout<<lmax[i-1]<<' '<<i<<' '<<rmax[i+1]);
			return 0;
		}
		
		if(w[lmin[i-1]]<w[i] && w[rmax[i+1]]<w[i])
		{
			cout<<3<<endl;
			cout<<lmin[i-1]<<' '<<i<<' '<<rmin[i+1]);
			return 0;
		}
	}
	
	puts("0");
	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值