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


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

    感想

    关于自己

    这一次想复杂了,第三题打了个较难的思路,导致时间过长。
    第四题一开始读错题了,调了半天才 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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    第二题 (难度:简单)

    题目描述

    现在有一截楼梯,根据你的腿长,你一次能走 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
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

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

    题目描述

    小艺酱又得到了一堆括号。 括号是严格匹配的。 现在给括号进行上色。 上色有三个要求:
    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111

    第四题(难度:中等)

    题目描述

    总是喜欢在水里嬉戏的青蛙,某天要过河拜访一位朋友。
    已知河道中长满了带刺的不知名生物,能通过的路只有一条直线,长度为 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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    100分做法

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

  • 相关阅读:
    中文分词的词典中的词性标记
    【JavaSE】String类
    原创 | 手把手带你玩转Apache MADlib
    数字电路基础-COMS电路静态、动态功耗,低功耗设计
    二十五、DSL查询文档(全文检索查询、精确查询、地理查询、复合查询)
    0基础 三个月掌握C语言(11)
    少儿编程对国内传统学科的推进作用
    线程入门java
    课堂问题:一个凸函数的性质
    45从零开始用Rust编写nginx,静态文件服务器竟然还有这些细节
  • 原文地址:https://blog.csdn.net/qq_42989972/article/details/128057545