大佬题解网址:https://blog.csdn.net/qq_45530271/article/details/103809647
原题目网址:https://vjudge.net/contest/350103#overview
目录
A.构造最长递增子串
题意:
给定一段序列,选其中连续的一段(ai,ai+1…,aj),最多更改其中的一个数为任意值,使其变为递增子串。求这样的递增子串的最大长度。简单来说,找到最长伪递增数列,其首位与末位相减差加一几位所求。
思路:
说实话没搞懂。借鉴大佬的思路说一下:
因为要找的序列元素是连续的,所以可以正序求出以ai 结尾的最长递增子串,倒序求出以ai 开头的最长递增子串。然后只需要从头到尾扫一遍,更改当前ai的值判断能不能使ai两边的串连起来(不能则舍掉一边),更新最大长度即可。
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<stdlib.h>
int a[100007],t1[100007],t2[100007];
//这里这个范围问题着重强调一下。原文中提到的ai的范围是1<=ai<=10^9,不是数组这个范围,读题!
int n,i,ans,max;
int main() {
while(~scanf("%d",&n)) {
a[0]=a[n+1]=t1[0]=t1[n+1]=t2[0]=t2[n+1]=ans=max=0;
for(i=1; i<=n; i++) scanf("%d",&a[i]);
for(i=1; i<=n; i++) {
if(a[i]>a[i-1])
t1[i]=t1[i-1]+1;
else t1[i]=1;
}
for(i=n;i>=1; i--) {
if(a[i+1]>a[i])
t2[i]=t2[i+1]+1;
else t2[i]=1;
}
//这里正序倒序的次序累计方法着重强调一下,符合条件每个等于上一个+1,否则重新归1,
//因为一开始我们空出首末位给0,所以正好可以使起始位为1(当然还有别的方法)。
for(i=1; i<=n; i++) {
if(a[i+1]-a[i-1]>1) ans=((t1[i-1]+t2[i+1]+1)>ans)?(t1[i-1]+t2[i+1]+1):ans;
//这里为类似 2 3 1 5 6的排序法,若为断层则将前后次序相加并加上中间的1
//对于a[i+1]-a[i-1]>1,可以理解为避免重复情况的大小判断
//(虽说输入应该都是按照规则的合法输入不可能出现重复)
else {
max=(t1[i-1]>t2[i+1])?t1[i-1]:t2[i+1];
max++;//去掉的加上
ans=(max>ans)?max:ans;
}
//else这里挺难搞懂的,类似 7 8 1 3或7 8 3 1 5 6;出现的原因为,断层正好处在两个不同递增序列的末与头,或者刚好有一个大于后者的序列完结,
//此时对此数前后序列进行大小比较,取较大者以达到最长序列的目的。
//一开始纠结过为什么不把这个位置的序列加进去,但想想,7 8 1 3中的8为前者序列中一员,1为后者中一员,可能出现的情况很多,不方便一一进行判断分类讨论,所以直接把此数去掉。最后找到最大者+1.
//也许有人会认为3个数7 8 1 会出现歧义 本来前者2大于1,可去掉8就1:1了,但到了一的时候还会再比一次,此时为2:1,max取2。
}
printf("%d\n",ans);
}
return 0;
}
B.Is it beautiful?
题意:
给定一个[1…n]的排列,对于前k个数,判断是否有一个区间[ai,ai+1,…,aj]使这个区间内恰好只有前k个数。(k依次从[1…n]取值)
思路:
耽误了很长时间的题走了很多弯路。直接说参照大佬的正确思路,反向储存数组,按大小储存位置。若一个数组为相连接不断开的区间,则其最高项和最低项的差+1的就应该恰好是所求答案(可以用线段来理解),中间若有断层,长度一定不符合正确答案。试着理解题目,重在理解对a1,a2的地址的储存的意义。详见下。
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<stdlib.h>
int a[200010],b[200010];
int main() {
int t;
scanf("%d",&t);
int n,i,l,r,x,min,max,c=0,j,p;
while(t--) {
scanf("%d",&n);
a[0]=a[n+1]=0;
for(i=1; i<=n; i++) {
scanf("%d",&x);
a[x]=i;
}
min=max=a[1];
//4 5 1 3 2 6
//a[1]=3,a[2]=5,a[3]=4.a[4]=1,a[5]=2,a[6]=6;
//101011
for(j=1; j<=n; j++) {
min=(a[j]<min)?a[j]:min;
max=(a[j]>max)?a[j]:max;
if(max-min+1==j) b[j]=1;
else b[j]=0;
}
//这里着重强调一下,这里一开始我用了一个二重循环,对每个要求的长度单位我都(麻烦的)重新从头判断遍历,
//切记一个二重循环时间超时是非常严重的,应巧妙地寻找避免使用二重的方法。
for(i=1; i<=n; i++) printf("%d",b[i]);
printf("\n");
}
return 0;
}
C.Juice
目前水准无法搞定。水平上去后回来补此题。
https://vjudge.net/contest/350103#problem/C
D Eat Candies
题意:
有三堆糖果,每天吃两块不同的糖果(必须是不同的两堆),求能吃的最多天数。
思路:
对数学思维要求较高,这里照搬大佬的思路。
有多种解决方法。
其中一种方法是先把三堆糖果从小到大排序。
1.先一直吃第一堆and第三堆,直到第三堆数量等于第二堆或者第一堆已吃完。
2.如果第一堆仍有剩余,将其均分为两份,第一份与第二堆同时吃,第二份与第三堆同时吃
3.最后同时吃第二堆and第三堆
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<stdlib.h>
int cmp(const void*a,const void*b) {
return *(int*)a-*(int*)b;
}
int main() {
int i,t;
scanf("%d",&t);
int a[3];
while(t--) {
int d=0,c;
scanf("%d%d%d",&a[0],&a[1],&a[2]);
qsort(a,3,sizeof(a[0]),cmp);
if(a[2]-a[0]>a[1]) {
d+=a[0]+a[1];
}//1 4 8
else {
d+=a[2]-a[1];
a[0]-=d;
d+=a[0]/2*2;
d+=a[1]-a[0]/2;
}
printf("%d\n",d);
}
return 0;
}
E.由你来决定怎么颁奖
题意
在满足所有奖牌数量条件下任意决定金银铜牌数量,奖牌数不超过人数一半,金牌小于银牌也小于铜牌,三者都大于0.
思路:
别想麻烦了,原题没有给出限制,方案很多一种即可。让金牌最少,银牌循环判断超过金牌即停,铜牌用差算。
(a了一晚上的原因:大小判断写错了)
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<stdlib.h>
int a[400007],c[400007];
int main() {
int n;
scanf("%d",&n);
while(n--) {
int num,de=0,i,j,p,q,g=0,s=0,b=0;
memset(a,0,sizeof(a));
scanf("%d",&num);
for(i=1; i<=num; i++)
for(i=1,j=1; i<=num; i++) {
scanf("%d",&c[i]);
if(i==1) {
a[1]++;
continue;
}
if(c[i]!=c[i-1]) {
j++;
a[j]++;
} else a[j]++;
}
for(i=1; i<=j; i++) {
if(de+a[i]<=num/2) de+=a[i];
else break;
}
g=a[1];
for(p=2; p<i-1; p++) {
s+=a[p];
if(s>g) break;
}
b=de-g-s;
if(g<=0||s<=0||b<=0||g>=s||g>=b) printf("0 0 0\n");
else printf("%d %d %d\n",g,s,b);
}
return 0;
}
F.XorXor
和d题同为数学思维题。涉及知识点未储备,同c题,在未来加以解答
G.0011
题意:
原始串“01”,可以在任意位置添加任意个“01”。
给n个串,问每个串是否能由上述方法合法得来。输出YES或NO。
(说个题外话,,当时完全没看出来原始串这个概念,一直在想怎么确定他插入的是个什么东西。。。)
原思路1 :串必定以0开头,从头储存0的数量,遇到第一个1时即停止。若从此1往后的连续1的数量等于相遇之前0的数量,此串即为合法。
错误原因:0000101111合法。
原思路2 :一个0必有一个相应的1与其对应。0必为开头,1必为结尾,两头开始走,相异时便进行一次数量比对,若相等即为合法继续往中间走。
错误原因:01110001非法。
思路:绝不可以有提前出现的1,再结合简化思路2
从头开始读取,遇0集合s++,遇1–,若出现s<0的情况就可判为非法跳出。若结束时s不为0也为非法。
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<stdlib.h>
int main() {
int i,n;
scanf("%d",&n);
while(n--) {
int s=0,p=1;
char a[100000];//注意此出字符串大小,大就完事了。
scanf("%s",a);
for(i=0; a[i]!='\0'; i++) {
if(a[i]=='0') s++;
else s--;
if(s<0) {
p=0;
break;
}
}
if(p==1&&s==0) printf("YES\n");
else printf("NO\n");
}
return 0;
}
H.Perfect String
题意:
将字符串中的?进行a或b或c的选择填充,以达到最终字符串任意两相邻字符不相等的效果。答案不唯一。如:abababababa
思路:
挺巧的这题,想明白非常简单。答案还不唯一。只要和前后不相等就可。
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<stdlib.h>
int main() {
int i,n;
scanf("%d",&n);
while(n--) {
int p=1;
char a[100050];
scanf("%s",a);
if(a[0]=='?'&&a[1]!='a') a[0]='a';
else if(a[0]=='?'&&a[1]=='a') a[0]='b';//避免首字母为?的情况
for(i=1; a[i]!='\0'; i++) {
if(a[i]==a[i-1]&&a[i]!='?') {
p=-1;
break;
}//先检索是否合法。
if((a[i+1]=='?'||a[i+1]=='\0')&&a[i]=='?') {
if(a[i-1]!='a') a[i]='a';
else a[i]='b';
} else if((a[i+1]!='?'&&a[i+1]!='\0')&&a[i]=='?') {
if(a[i-1]!='a'&&a[i+1]!='a') a[i]='a';
else if(a[i-1]!='b'&&a[i+1]!='b') a[i]='b';
else if(a[i-1]!='c'&&a[i+1]!='c') a[i]='c';
}
}
if(p==1) printf("%s\n",a);
else printf("-1\n");
}
return 0;
}
I. 十进制中的二进制
题意:
找从1到n的所有数中仅由0和1构成的数的数量。
思路:
由1和0组成自然而然就得联想到二进制,而当把所有二进制数目一个个写出来的时候你就能发现一个很巧妙的规律。
比方说1=1,10=2,11=3,100=4,等于的十进制数目正好等于已存在的二进制数字的数目(比方说,4代表有了四个数字了),所以此题只需寻找n以下所靠最近二维数组所代表的十进制数目即为十进制中可用二进制表达的数目。
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<stdlib.h>
int f(int n);//此函数用来将十进制化为二进制。
int main() {
int n,m;
while(~scanf("%d",&n) ){
int m=0;
while(f(m+1)<=n) m++;
printf("%d\n",m);
}
return 0;
}
int f(int n) {//基本思路可以规整为,每次除二的余数倒序排列。
int a,s=0,p=1;
do{
a=n%2;
n=n/2;
s+=a*p;
p=p*10;
}while(n!=0);//牢记十进制转二进制的转换思路。
return s;
J.水题
题意:
给梯形四个点坐标,求梯形的面积。
思路:
上底加下底括起来乘高除以二
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<stdlib.h>
int main(){
double x,y,z,t;
while(~scanf("%lf%lf%lf%lf",&x,&y,&z,&t)){
printf("%.1lf\n",0.5*(z-x)*(y+t));
}
}
总结
这次周赛发挥不好,成绩丢人到懒得说了。也许对自己来说这次题目稍微偏难。但是题解写出来可以看出来至少有很多题目可以通过巧妙地思路来化解,而这样一份巧妙地思路往往来自大量的题海积累。所以说到底还是题做少了。
总结如下:
1.不要惧怕大篇幅题目(实在不行以后就逮着哪个题干长做哪个)
2.读懂题目!理解题目!彻底读明白题目!
3.若出现wa,往往来自读题的不仔细,一定要认真读题,严格确定范围的大小和大小比较的关系。
4.一定要避免二重循环的出现,超时太严重了。而避免的方法往往都很巧妙。
5.刷题刷题刷题。
6.补题、写题解还是很有用的,一定要坚持下去
7.早写题解,,,早日写完没心事,,,不然就得熬到现在。。。(太晚了睡了睡了