【CSDN竞赛】第十期解题报告

文章目录

  • 感想
      • 关于自己
      • 关于平台
  • 第一题 (难度:入门)
      • 题目描述
      • 100分做法
  • 第二题 (难度:简单)
      • 题目描述
      • 100分做法
  • 第三题 (难度:中等/困难)
      • 题目描述
      • 100分做法1(对应中等)
      • 100分做法2(对应困难)
  • 第四题(难度:中等)
      • 题目描述
      • 90分做法
      • 100分做法

这应该是站内相对详细的解题报告吧。
转载思路请@本人。

感想

关于自己

这一次想复杂了,第三题打了个较难的思路,导致时间过长。
第四题一开始读错题了,调了半天才 90 90 90分。
最终得分 97.5 97.5 97.5,痛失前五 Q A Q QAQ QAQ
自己的问题,下次时间要均匀分配一些,不要想复杂了(毕竟 C S D N CSDN CSDN的题目难度大多都不高)。
听说有人抱怨考第一就作弊,其实十几分钟打完我都不信,作弊也说得过去。

关于平台

有一个地方:每次测评和提交都有冷却时间(痛,太痛了)。
求求系统冷却快一点吧,好浪费时间的(特别是冷却 15 15 15秒)。
题目质量再次上升,但不多。
下次什么时候出点数据结构,让前几名的去水水?
其实可以向社会征集题目,然后经过查重和验题放进题库,不过提供题目的人通过就不能增加做题数量或参加比赛(我个人的建议)。

第一题 (难度:入门)

题目描述

已知存在一个长度为 n n n的整数序列 A A A A A A中所有元素按照从小到达的顺序进行排序。 现在执行操作倒置一段序列。 请找到 A A A序列里的倒置子序列。

100分做法

因为只有一段序列不同,所以找到最左边不相同的和最右边不相同的(与排序后相比),然后输出整段区间即可。

C + + C++ C++代码如下:

#include
using namespace std;
int a[1005]={},b[1005]={},c[1005]={},n,l=1000,r=0;
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		b[i]=c[i]=a[i];
	}
	sort(a+1,a+n+1);
	for(int i=1;i<=n;i++)
	{
		if(b[i]!=a[i])b[i]=0;
		else b[i]=1;
	}
	for(int i=1;i<=n;i++)if(b[i]==0){l=min(l,i);r=max(r,i);}
	if(l==1000)l=0;
	printf("%d %d\n",l,r);
	return 0;
}

第二题 (难度:简单)

题目描述

现在有一截楼梯,根据你的腿长,你一次能走 1 1 1级或 2 2 2级楼梯,已知你要走 n n n级楼梯才能走到你的目的楼层,请实现一个方法,计算你走到目的楼层的方案数。

100分做法

典型的递推题,也是斐波那契数列,具体思路学了递推就知道了。

C + + C++ C++代码如下:

#include
using namespace std;
long long a[100];
int n;
int main()
{
	a[0]=a[1]=1;
	scanf("%d",&n);
	for(int i=2;i<=n;i++)
	{
		a[i]=a[i-1]+a[i-2];
	}
	printf("%lld\n",a[n]);
}

第三题 (难度:中等/困难)

题目描述

小艺酱又得到了一堆括号。 括号是严格匹配的。 现在给括号进行上色。 上色有三个要求:
1、只有三种上色方案,不上色,上红色,上蓝色。
2、每对括号只有一个上色。
3、相邻的两个括号不能上相同的颜色,但是可以都不上色。
问括号上色有多少种方案?答案对 1000000007 1000000007 1000000007取模。

100分做法1(对应中等)

d p i , j , 0 / 1 dp_{i,j,0/1} dpi,j,0/1表示 [ i , j ] [i,j] [i,j]区间内,左右两边括号是否匹配,上色方案数。
这个方法看起来比较简单,我没认真研究,具体可以参考BMTXLRC大佬的博客。

100分做法2(对应困难)

前置知识:树形dp。
我们发现,题目给出的括号序列可以通过以下方式构成

  1. 假设 A A A是合法的括号序列,则 ( A ) (A) (A)是合法的括号序列;
  2. 假设 A , B A,B A,B是合法的括号序列,则 A B AB AB是合法的括号序列。

其中,第一种对应树中的父子关系,第二种对应树中的兄弟关系。
于是,我们可以按照上述思路,将括号序转成一棵树。
这样就变成了在树上做 d p dp dp,也就是树形 d p dp dp
d p i , 0 / 1 / 2 / 3 , 0 / 1 / 2 / 3 dp_{i,0/1/2/3,0/1/2/3} dpi,0/1/2/3,0/1/2/3表示 i i i子树内,左右两边不上色/上红/上蓝的方案数。
按照题目的限制和括号序列构成方式转移即可。

C + + C++ C++代码如下:

#include
using namespace std;
int fst[100005]={},nxt[100005]={},lst[100005]={},To[100005]={};
long long dp[1005][3][3]={};
int st[100005]={};
char s[1005]={};
int n,top,cnt=0,ct=1,rt=1;
long long mod=1000000007;
void add(int x,int i)
{
	if(!fst[x])
	{
		fst[x]=i;
		lst[x]=i;
	}
	else
	{
		nxt[lst[x]]=i;
		lst[x]=i;
	}
}
void dfs(int x)
{
	bool ok=0;
	for(int i=fst[x];i;i=nxt[i])
	{
		int v=To[i];
		dfs(v);
		if(ok==0)
		{
			ok=1;
			for(int j=0;j<=2;j++)for(int k=0;k<=2;k++)dp[x][j][k]=dp[v][j][k];
			continue;
		}
		long long u[3][3]={};
		for(int j=0;j<=2;j++)for(int k=0;k<=2;k++)u[j][k]=dp[x][j][k],dp[x][j][k]=0;
		for(int j=0;j<=2;j++)
		{
			for(int k=0;k<=2;k++)
			{
				for(int l=0;l<=2;l++)
				{
					for(int p=0;p<=2;p++)
					{
						if((k==0&&l==0)||(k!=l))dp[x][j][p]=(dp[x][j][p]+u[j][k]*dp[v][l][p])%mod;
					}
				}
			}
		}
	}
	if(ok==0)
	{
		dp[x][0][1]=dp[x][0][2]=dp[x][1][0]=dp[x][2][0]=1;
	}
	else
	{
		if(x==1)
		{
			long long u=0;
			for(int i=0;i<=2;i++)for(int j=0;j<=2;j++)
			{
				u=(u+dp[x][i][j])%mod;
			}
			dp[x][0][0]=u;
		}
		else
		{
			long long u[3][3]={};
			for(int i=0;i<=2;i++)for(int j=0;j<=2;j++)u[i][j]=dp[x][i][j],dp[x][i][j]=0;
			for(int i=0;i<=2;i++)
			{
				for(int j=0;j<=2;j++)
				{
					for(int k=0;k<=2;k++)
					{
						for(int l=0;l<=2;l++)
						{
							if((i==0&&j==0)||(i!=j))
								if((k==0&&l==0)||(k!=l))
									if((i==0&&l!=0)||(l==0&&i!=0))dp[x][i][l]=(dp[x][i][l]+u[j][k])%mod;
						}
					}
				}
			}
		}
	}
}
int main()
{
	cin>>s;
	n=strlen(s);
	st[top]=1;
	for(int i=0;i<n;i++)
	{
		if(s[i]=='(')
		{
			To[++cnt]=++ct;
			add(st[top],cnt);
			top++;
			st[top]=ct;
		}
		else if(s[i]==')')
		{
			st[top]=0;
			top--;
		}
	}
	dfs(1);
	printf("%lld\n",dp[1][0][0]);
	return 0;
}

第四题(难度:中等)

题目描述

总是喜欢在水里嬉戏的青蛙,某天要过河拜访一位朋友。
已知河道中长满了带刺的不知名生物,能通过的路只有一条直线,长度为 L L L
直线上随机分布着 m m m块石头。青蛙的最小跳跃距离是 s s s,最大跳跃距离是 t t t

  • 青蛙可以在 [ 0 , i n f ] [0,inf] [0,inf]区间的任意地方停留(包括没有石头的地方)。

青蛙想要尽可能的少踩石头,那么它通过河道最少会踩到多少石头?

90分做法

上面带点的句子是我补上去的,当时理解错题意了。
具体就是先离散化,然后就和一般的 d p dp dp一样了。
d p i dp_{i} dpi表示到达数轴点 i i i(不是输入的第 i i i个点),最少踩的石头数。
枚举区间然后转移即可。

C + + C++ C++代码如下:

#include
using namespace std;
int a[1000005]={},b[1000005]={},c[10000005]={},dp[10000005]={};
int L,m,s,t;
int main()
{
	while(scanf("%d",&L)!=EOF)
	{
		scanf("%d%d%d",&s,&t,&m);
		a[0]=0;
		for(int i=1;i<=m;i++)
		{
			scanf("%d",&a[i]);
		}
		a[++m]=L;
		sort(a+1,a+m+1);
		for(int i=1;i<=m;i++)
		{
			b[i]=a[i]-a[i-1];
			if(b[i]>4320)b[i]=4320;
		}
		for(int i=1;i<=m;i++)
		{
			a[i]=a[i-1]+b[i];
		}
		for(int i=0;i<=a[m]+20;i++)dp[i]=1e9,c[i]=0;
		for(int i=1;i<=m;i++)c[a[i]]++;
		dp[0]=0;
		for(int i=0;i<=a[m]+20;i++)
		{
			for(int j=s;j<=t;j++)
			{
				dp[i+j]=min(dp[i+j],dp[i]+c[i]);
			}
		}
		int ans=1e9;
		for(int i=a[m];i<=a[m]+20;i++)
		{
			ans=min(ans,dp[i]);
		}
		printf("%d\n",ans);
	}
	return 0;
}

100分做法

调调细节应该就可以了。
应该是离散化间距的问题。

你可能感兴趣的:(c++主要资料,考试,练习,c++)