蓝桥杯史上最详细C/C++解析(经典题解)

什么是蓝桥杯?
官方解释是:蓝桥杯大赛是工信部人才交流中心举办的全国性专业信息技术赛事。这个比赛最早是针对大学生群体的编程赛事。
自从2016年第八届蓝桥杯开始,在原有的大学生数个专业编程组别的基础上增加了中小学创意编程组,简称【青少组】,第11届竞赛,超过4万名中小学参加了青少年组的比赛,第12届起,STEMA评测考试替代了青少年组的地区选拔赛,主要是评测学生的【科技素养】、【逻辑思维】、【编程能力】按照省份给出综合评测成绩。
简单讲就是这是一个编程领域专业性的全国比赛,初赛选拔是考STEMA(科学(Science)、技术(Technology)、工程(Engineering)、数学(Mathematics)、艺术(Arts))
对的,除了考编程还需要考一些“课外知识”,两部分分数占比是五比五,同等重要。

先给大家说说蓝桥杯的大概比赛时间吧:比赛分为初赛(STEMA选拔赛)——省赛——国赛
初赛一年五到六次,一般是每年8,10,11,1,3这几个月份进行。四月份省赛,五月份国赛。
然后是晋级规则:
一等奖晋级机制:初赛一等奖可以直接进国赛;初赛二等奖,三等奖,优秀奖,需要参加第二轮的省赛;省赛一等奖才能晋级国赛。
总的来说,如果想走到全国总决赛,要么初赛是一等奖,要么省赛是一等奖,相当于给了每个考生2次机会。

第一题:结点选择

题干:
有一棵 n 个节点的树,树上每个节点都有一个正整数权值。如果一个点被选择了, 那么在树上和它相邻的点都不能被选择。求选出的点的权值和最大是多少?

解题思路: 这题模型是树形动态规划入门题目, dp[i][0]表示该节点不被选择,dp[i][1]表示该结点被选择。 转移方程为: dp[u][1]+=dp[v][0];//选择了 u 结点,则与它邻接的结点不选; dp[u][0]+=max(dp[v][0],dp[v][1]);不选择 u 结点,则与它邻接的结点选择结果最 大的; 应该特别注意:该题结点数量较大,应该选用邻接表存储边的关系。

#include<cstdio>
#include<cstring>
#define max(a,b) ((a)>(b)?(a):(b))
#define maxn 100010

bool vis[maxn];
int dp[maxn][2];
int father[maxn];
int head[maxn];
int n;
int cnt;
struct Edge
{
	int to,next;
}edge[2*maxn];

void add(int u,int v)
{
	edge[cnt].to=v;
	edge[cnt].next=head[u];
	head[u]=cnt++;
}

void treedp(int u)
{
	vis[u]=1;
	for(int i=head[u];i!=-1;i=edge[i].next)
	{
		int v=edge[i].to;
		if(!vis[v])
		{
			treedp(v);
			dp[u][1]+=dp[v][0];
			dp[u][0]+=max(dp[v][1],dp[v][0]);
		}
	}
}

void init()
{
	cnt=0;
	memset(dp,0,sizeof(dp));
	memset(father,0,sizeof(father));
	memset(vis,0,sizeof(vis));
	memset(head,-1,sizeof(head));
}

int main()
{
	init();
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	scanf("%d",&dp[i][1]);
	int root=0;
	int begin=1;
	for(int i=0;i<n-1;i++)
	{
		int a,b;
		scanf("%d%d",&a,&b);
		add(a,b);
		add(b,a);
		father[b]=a;
		if(root==b||begin)
		{
			root=a;
		}
	}
	while(father[root])
	root=father[root];
	treedp(root);
	int ans;
	ans=max(dp[root][0],dp[root][1]);
	printf("%d\n",ans);
}

第二题:K 好数

题干:
如果一个自然数 N 的 K 进制表示中任意的相邻的两位都不是相邻的数字,那么我们就 说这个数是 K 好数。求 L 位 K 进制数中 K 好数的数目。例如 K = 4,L = 2 的时候,所有 K好数为 11、13、20、22、30、31、33 共 7 个。由于这个数目很大,请你输出它对 1000000007 取模后的值。

解题思路:
dp[i][j]表示第 i 位为 j 的时候的个 I 好数个数;
因此有转移方程:
dp[i][j]=dp[i-1][m]+dp[i][j];m 为上一位的值,满足的条件应为 m-j 的绝对值不为 1.即不相邻;

应当注意的是:最后在求和的时候不能简单的统计 dp[l][m] 0<=m<k;因为首位如果是 0 的话,其实不足 L 位了,所以 0<m<k,也许有人会疑问这是不统计 L 位的 0,不是第一位 呀。。。。。。其实仔细想想,是等效的。

#include<cstdio>
#include<cstring>
#define mod 1000000007
#define abs(a,b) ((a)>(b)?(a-b):(b-a))

int dp[110][110];
int main()
{
	int k,l;
	memset(dp,0,sizeof(dp));
	scanf("%d%d",&k,&l);
	for(int i=0;i<k;i++)
	dp[1][i]=1;
	for(int i=2;i<=l;i++)
	{
		for(int j=0;j<k;j++)
		{
			for(int m=0;m<k;m++)
			{
				if(abs(m,j)!=1)
				dp[i][j]=(dp[i][j]%mod+dp[i-1][m]%mod)%mod;
			}
		
		}
	}
	
	int ans=0;
	for(int i=1;i<k;i++)
	ans=(ans%mod+dp[l][i]%mod)%mod;
	printf("%d\n",ans);
}

第三题:K 好数

题干:
有 n 个格子,从左到右放成一排,编号为 1-n。
共有 m 次操作,有 3 种操作类型: 1.修改一个格子的权值, 2.求连续一段格子权值和, 3.求连续一段格子的最大值。 对于每个 2、3 操作输出你所求出的结果。

解题思路: 这是线段树的入门题目。直接建立线段树,求解就 OK 了

#include<cstdio>
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define N 100010
#define L(a) (a)<<1
#define R(a) (a)<<1|1

struct node
{
	int l,r,maxn,minn,sum;
}line[3*N];

int num[N];
int first,second;

void create(int k,int x,int y)
{
	line[k].l=x;line[k].r=y;
	if(x==y)
	{
		line[k].sum=num[x];
		line[k].maxn=line[k].minn=num[x];
		return;
	}

	int mid=(x+y)>>1;
	create(L(k),x,mid);
	create(R(k),mid+1,y);
	line[k].maxn=max(line[L(k)].maxn,line[R(k)].maxn);
	 //line[k].minn=mymin(line[L(k)].minn,line[R(k)].minn);
	line[k].sum=line[L(k)].sum+line[R(k)].sum;
}

void updata(int k,int a,int b)
{
	if(line[k].l==line[k].r)
	{
		line[k].maxn=b;
		line[k].sum=b;
		
		return ;
	}
	int mid=(line[k].l+line[k].r)>>1;
	if(a<=mid)
	updata(L(k),a,b);
	else
	updata(R(k),a,b);
	
	line[k].sum=line[L(k)].sum+line[R(k)].sum;
	line[k].maxn=max(line[L(k)].maxn,line[R(k)].maxn);
}

void quary(int k,int x,int y)
{
	if(line[k].l==x&&line[k].r==y)
	{
		second+=line[k].sum;
		first=max(first,line[k].maxn);
		return;
	}
	
	int mid=(line[k].l+line[k].r)>>1;
	if(y<=mid)
	quary(L(k),x,y);
	else if(x>mid)
	quary(R(k),x,y);
	else
	{
		quary(L(k),x,mid);
		
		quary(R(k),mid+1,y);
	}
}

int main()
{
	int n,m;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
	scanf("%d",&num[i]);
	create(1,1,n);
	for(int i=1;i<=m;i++)
	{
		int a,b,c;
		scanf("%d%d%d",&a,&b,&c);
		if(a==1)
		{
			updata(1,b,c);
		}
		else if(a==2)
		{
			first=-10000;
			second=0;
			quary(1,b,c);
			printf("%d\n",second);
		}
		else
		{
		
			first=-10000;
			second=0;
			quary(1,b,c);
			printf("%d\n",first);
		}
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值