赛题解析[CSP2020 J2]

本文介绍了四个算法问题的解决方案:优秀拆分涉及找到最大2的幂次并计算剩余部分;直播获奖利用桶排序计算获奖分数线;表达式计算涉及构建逻辑表达式树并动态更新结果;方格取数则关注二维矩阵中最大整数和的计算策略。这些题目涵盖了数学逻辑、数据处理和算法设计等核心概念。


一、优秀的拆分

题目描述:
在这里插入图片描述
在这里插入图片描述
解题思路:
给定正整数a,计算不超过a的最大的2的正整数次幂h,返回剩余部分a-h,直到返回部分等于0为止。

参考代码:

#include<bits/stdc++.h>
using namespace std;

//计算不超过a的最大的2的正整数次幂
int f(int a)
{
	int h=(int)(log(a)/log(2));
	h=(1<<h);
	cout<<h<<' ';
	return a-h;
}

int main()
{		
	freopen("power.in","r",stdin);
	freopen("power.out","w",stdout);
	int a;
	cin>>a;
	if (a%2==1)  //奇数不存在优秀拆分
		cout<<-1;
	else   
		while(a>0)
			a=f(a);	
}

二、直播获奖

题目描述:
在这里插入图片描述
在这里插入图片描述

解题思路:
(1)采用桶排序思想,对所有选手成绩进行计数,成绩的取值范围是0~600。
(2)根据获奖率和已评出的选手成绩,计算当前获奖分数线。

参考代码:

#include<bits/stdc++.h>
#define N 601
#define M 100000
using namespace std;

int t[M];
int h[N];

int main()
{
	freopen("live.in","r",stdin);
	freopen("live.out","w",stdout);
	int n,w;
	int i,j,k,p,r;		
		
	cin>>n>>w;
	//计算计划获奖人数
	for (p=1;p<=n;p++)
	{	
		r=p*w/100;
		if (r==0)
			t[p-1]=1;
		else
			t[p-1]=r;		
	}	
    
    //输入当前成绩,输出获奖分数线
	for (i=0;i<n;i++)
	{
		cin>>r;
		h[r]++;	//计算当前成绩的次数
		j=N;
		k=h[j];
		while(k<t[i])   //计算获奖分数线
		{
			j--;
			k=k+h[j];
		}	
		cout<<j<<' ';
	}
}

三、表达式

题目描述:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

解题思路:
(1)构建逻辑表达式计算树(可采用静态数组),根据表达式各变量的初始值计算表达式结果。
(2)针对每一次变量值修改,沿该叶子节点往上计算,通过比较每次计算结果,判断表达式最终结果是否改变,并输出表达式的值。

参考代码:

#include<bits/stdc++.h>
using namespace std;

char expr[1000001];
int t[500000][5];
int s[100000][2];
int n, q, k, m;

//构建表达式计算树	
int build()
{
	int j=-1;
	int i=0;	
	int x,l,r;
	k=0;
	//读表达式,构建二叉树(静态数组)
	while(expr[i]!=0)
	{
		if ( expr[i]<='9' && expr[i]>='0' )
		{
			x=0;
			while( expr[i]<='9' && expr[i]>='0' )
			{
				x = x*10 + expr[i]-'0';
				i++;
			}
			j++;
			s[j][0]=x-1;
		}
		else if (expr[i] == '&')
		{
			t[n+k][4]=-1;
			l=s[j-1][0];
			r=s[j][0];
			t[n+k][1]=-1;
			t[n+k][2]=l;
			t[n+k][3]=r;
			t[l][1]=n+k;
			t[r][1]=n+k;			
			t[n+k][0] = (t[l][0] && t[r][0]);
			j--;
			s[j][0]=n+k;
			k++;
			i++;
		}
		else if (expr[i] == '|')	
		{
			t[n+k][4] = -2;
			l=s[j-1][0];
			r=s[j][0];
			t[n+k][1]=-1;
			t[n+k][2]=l;
			t[n+k][3]=r;
			t[l][1]=n+k;
			t[r][1]=n+k;					
			t[n+k][0] = (t[l][0] || t[r][0]);
			j--;
			s[j][0]=n+k;
			k++;				
			i++;
		}
		else if (expr[i] == '!')
		{
			t[n+k][4] = -3;			
			l=s[j][0];
			t[n+k][1]=-1;
			t[n+k][2]=l;
			t[n+k][3]=-1;
			t[l][1]=n+k;			
			t[n+k][0] = 1-t[l][0];
			s[j][0]=n+k;
			k++;
			i++;			
		} 
		else
			i++;			
	}
	return t[n+k-1][0];
}

//改变变量真值,计算表达式运算结果
void calc(int h)
{
	int p=t[h][1];
	int l,r,v;
	int d=0;
	s[d][0]=h;
	s[d][1]=1-t[h][0];

	//从节点r的父节点开始,往上计算
	while(1)
	{
		if (t[p][4]==-1)
		{
			l=t[p][2];
			r=t[p][3];
			if (s[d][0]==l)
				v= s[d][1] && t[r][0];
			else
				v= t[l][0] && s[d][1];			
		}
		else if (t[p][4]==-2)
		{
			l=t[p][2];
			r=t[p][3];
			if (s[d][0]==l)
				v= s[d][1] || t[r][0];
			else
				v= t[l][0] || s[d][1];				
		}
		else
			v= 1-s[d][1];					
		
		if (v==t[p][0])
		{
			cout<<m<<endl;
			break;
		}
		else if (t[p][1]==-1) 
		{
			cout<<v<<endl;
			break;			
		}		
		else
		{
			d++;
			s[d][0]=p;
			s[d][1]=v;				
			p=t[p][1];
		}			
	}	
}

int main()
{
	freopen("expr.in","r",stdin);
	freopen("expr.out","w",stdout);
	
	gets(expr);	
	
	cin>>n;
	for (int i=0;i<n;i++)
	{
		cin>>t[i][0];
		t[i][2]=-1;
		t[i][3]=-1;
		t[i][3]=0;
		t[i][4]=0;
	}
	
	m=build();

	cin>>q;
	int r; 
	for (int i=0;i<q;i++)
	{
		cin>>r;		
		calc(r-1);	
	}	
}

四、方格取数

题目描述:
在这里插入图片描述
在这里插入图片描述

解题思路:
到达任一方格的最大整数和,取决于该方格与三个邻近方格(左、上、下)相加的最大值。
(1)对于第1列,仅考虑向下累加;
(2)对于其它列,先计算向右累加,然后计算向下,向上累加,比较三个方向的累加值,取最大值。

参考代码:

#include<bits/stdc++.h>
using namespace std;

int a[1000][1000];
long long b[1000][1000];
long long c[1000];
long long d[1000];
int n,m;

//向右方向计算
void right(int j)
{
	for (int i=0;i<n;i++)
		b[i][j]=b[i][j-1]+a[i][j];
}

//向下方向计算
void down(int j)
{
	d[0]=b[0][j];
	for (int i=1;i<n;i++)
		if (b[i-1][j]>d[i-1])
			d[i]=b[i-1][j]+a[i][j];
		else
			d[i]=d[i-1]+a[i][j];
}

//向上方向计算
void up(int j)
{
	c[n-1]=b[n-1][j];
	for (int i=n-2;i>=0;i--)
		if (b[i+1][j]>c[i+1])
			c[i]=b[i+1][j]+a[i][j];	
		else
			c[i]=c[i+1]+a[i][j];	
}

//比较三个方向的最大值
void comp(int j)
{
	for (int i=0;i<n;i++)
	{
		if (c[i]>b[i][j])
			b[i][j]=c[i];
		if (d[i]>b[i][j])
			b[i][j]=d[i];	
	}
}

int main()
{		
	freopen("number.in","r",stdin);
	freopen("number.out","w",stdout);
	
	cin>>n;
	cin>>m;		
	for (int i=0;i<n;i++)
		for (int j=0;j<m;j++)
			cin>>a[i][j];
    
    //第1列累加
	long long s=0;
	for (int i=0;i<n;i++)
	{
		b[i][0]=s+a[i][0];
		s=b[i][0];
	}	
	
	//其它列计算
	for (int j=1;j<m;j++)
	{
		right(j);
		down(j);
		up(j);
		comp(j);	
	}
	//输出最后格子的值
	cout<<b[n-1][m-1];
}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值