第十三届蓝桥杯c++b组2022年国赛决赛题解

 编程题题目就不放了,各位到下面刷题链接看吧 

2022年第十三届蓝桥杯大赛软件类决赛C/C++大学B组真题 - 题库 - C语言网 (dotcpp.com)

参考文献 

(16条消息) 第十三届蓝桥杯大赛软件赛决赛(C/C++ 大学B组)_将2022拆分成10个不同的正整数_肖有量的博客-CSDN博客

(16条消息) 第十三届蓝桥杯c++b组2022年国赛决赛题解_将2022拆分成10个不同的正整数_int 我的博客-CSDN博客

2022

 上来就dfs,但不好意思,搜不出来,因为太大了

ll sum,cnt,target;
void dfs(int begin,int index)
{
	if(begin>target)return ;
	if(sum>target)return ;
	if(index==10)
	{
		//cout<<sum<<endl;
		if(sum==target)
		{
			cout<<begin<<endl;
			cnt++;
		}
		return ;
	}
	for(int i=begin;i<=target;i++)
	{
		sum+=i;
		dfs(i+1,index+1);
		sum-=i;
	}
} 
int main() 
{
	target=2022;
	dfs(1,0);
	cout<<cnt;
	return 0;
}

本题就是01背包模板题,只不过原来的背包容量扩充到了两个属性,因此增加一个维度表示另一个属性 

(16条消息) 10数之和为2022_蓝桥杯 10个数相加组成2022_哆啦刘小洋的博客-CSDN博客

#include<bits/stdc++.h>
using namespace std;
const int MAX=2e5+10;
typedef long long ll;
#define x first 
#define y second 
typedef pair<int,int>PII;
ll sum,cnt,target;
ll dp[2500][20]; 
/*dp[i][j]表示选j个数总和为i的方案数
dp[i][j]=dp[i-1][j-1]+dp[i-2][j-1]+.....+dp[1][j-1] 
本题就是01背包模板题,只不过原来的背包容量扩充到了两个属性,因此增加一个维度表示另一个属性 
*/
int main()
{
	dp[0][0] = 1;//初始化 0个数组成0当然1种情况 
	/*01滚动数组,先物品后背包 背包要倒叙(否则重复装入)*/
	for (int i = 1; i <= 2022; i++)  //遍历物品 2022种物品,每种物品体积等于i
	{
		for(int k=2022;k>=i;k--)//遍历第一维背包——总和 
		for (int j = 10; j>=1; j--)   //遍历第二维背包——个数 
				dp[k][j] += dp[k-i][j-1];   //前i种物品,取j个物品(每种物品只有一个,该物品体积等于i),物品总体积等于k的总方案
	}//对于dp[j][k]的方案,等价于不取i物品,取j个数,总体积恰好为k的方案加上取物品,i取j个数,总体积恰好为k方案
	cout<<dp[2022][10];
 
    return 0;
}

钟表 

穷举

要知道钟表问题的角度求法

#include<bits/stdc++.h>
using namespace std;
const int MAX=2e5+10;
typedef long long ll;
#define x first 
#define y second 
typedef pair<int,int>PII;
double myabs(double angle)
{//夹角是取较小的角 
    angle=abs(angle);
    if(angle>180)angle=360-angle;    
    return angle;
} 
int main()
{
    for(double h=0;h<6;h++)
    {
        for(double m=0;m<60;m++)
        {
            for(double s=0;s<60;s++)
            {
                double angs=(s/60)*360; //秒针在时钟上的角度 一圈360 s/60为0.x圈 
                double angm=(m/60+s/(60*60))*360;  //分针在时钟上的角度 分针贡献+秒针贡献
                double angh=(h/12+m/(60*12)+s/(12))*360;//时针角度  时针贡献+分针贡献+秒针贡献
                double A=myabs(angh-angm);//时分针角度
                double B=myabs(angm-angs);//分秒针角度
                if(abs(A-2*B)<1e-3)//高精度相等不直接用==而是用这个 
                cout<<h<<" "<<m<<" "<<s<<endl; 
            }
        }
    }
    return 0;
}


 
卡牌

看到最大最小题往二分想想,别忘开long long!!!

#include<bits/stdc++.h>
using namespace std;
const int MAX=2e5+10;
typedef long long ll;
#define x first 
#define y second 
typedef pair<int,int>PII;
ll a[MAX],b[MAX],temp[MAX];
ll n,m;
bool check(ll mid)
{//检查是否能凑出mid张牌 
	ll num=m;//空白牌数量
	memcpy(temp,b,sizeof(b)); //注意别把b改变了 
	for(ll i=1;i<=n;i++)
	{
		if(a[i]<mid)
		{//需要给i分空白牌 mid-a[i]张 
			temp[i]-=mid-a[i];
			num-=mid-a[i];
			if(temp[i]<0||num<0)return false;//不够分的  
		}
	}
	return true;
}
int main()
{
	cin>>n>>m;
	ll minval=INT_MAX,maxval=0;
	for(int i=1;i<=n;i++) 
	{
		cin>>a[i];
		minval=min(minval,a[i]);
		maxval=max(maxval,a[i]);
	}
	for(int i=1;i<=n;i++) 
		cin>>b[i];	
		
	/*本题是求最大最小问题 ,可以使用二分,把解(凑出的牌数)二分 ,
	每次check,如果能分出来,答案就在mid,right,分不出来答案在left,mid-1*/
	int left=minval,right=maxval;
	while(left<right)
	{
		ll mid=(left+right+1)/2;
		if(check(mid))left=mid;
		else right=mid-1;
	}
	cout<<left;
    return 0;
}

最大数字

dfs+贪心剪枝优化+字符串存储数字

#include<bits/stdc++.h>
using namespace std;
const int MAX=2e5+10;
typedef long long ll;
#define x first 
#define y second 
typedef pair<int,int>PII;
string  num,res;
int A,B,n;
/*贪心性质 :每一位最优的化一定是只用+,或者只用- ,不可能即用+又用- 
dfs+贪心剪枝优化 :处理每一位尽可能优,深搜处理下一位,到头回溯采用另一种方法*/
void dfs(int a,int b,int index)
{//在处理num的第index位, a b是A B剩余数量 
	if(index==n||(a<=0&&b<=0))//最后一位已经处理完 或者a,b都没有了 
	{
		res=max(res,num);
		return ;
	}
	int val=num[index]-'0'; 
	if(a>=9-val)
	{//+够用的用+到9
		num[index]='9'; 
		dfs(a-(9-val),b,index+1);
		num[index]=val+'0';
	}
	else
	{//不能+到9则尽可能+ ,即a全用 
		num[index]+=a;
		dfs(0,b,index+1);
		num[index]=val+'0'; 
	}
	
	if(b>=val+1)
	{//用b:能减到9就用,(否则一定比原来小) 
		num[index]='9'; 
		dfs(a,b-(val+1),index+1);
		num[index]=val+'0';
	}
}
int main()
{
	cin>>num>>A>>B;
	n=num.size();
	dfs(A,B,0);
	cout<<res;
    return 0;
}

出差

Dijkstra模板题

#include<bits/stdc++.h>
using namespace std;
const int MAX=1e3+10;
typedef long long ll;
#define x first 
#define y second 
#define INF 999999
typedef pair<int,int>PII;
int graph[MAX][MAX]; //城市数量不大 可以直接开二维
int days[MAX],n,m; //到某个城市的隔离期 
int used[MAX],cost[MAX];
//i点最短路径是否找到  存储1-i最短时间  
int Dijkstra()
{
	used[1]=1;
	for(int i=2;i<=n;i++)
	{//cost初始化结点1的所有边 
		cost[i]=graph[1][i];
	}
	for(int i=1;i<=n;i++)
	{
		int minval=INF,k=n;
		for(int j=2;j<=n;j++)
		{//遍历找到cost集合最短边及其顶点
			if(!used[j]&&cost[j]<minval)
			{
				minval=cost[j];
				k=j;	
			}	
		} 
		used[k]=1; //1->k最短路径找到		
		for(int j=2;j<=n;j++)
		{
			if(!used[j]&&cost[k]+graph[k][j]<cost[j])
			{//经过k到j,比直接到j小,更新 
				cost[j]=cost[k]+graph[k][j];
			}
		}
	}
	return cost[n]-days[n]; //到n点不需要在加上隔离期 
}
int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)cin>>days[i];
	for(int i=1;i<=n;i++)
	{//注意没有路的边一定要初始无穷 否则在0边肯定小,出错 
		for(int j=1;j<=n;j++)
			graph[i][j]=INF;
	}
	for(int i=1;i<=m;i++)
	{
		int a,b,val;
		cin>>a>>b>>val;
		graph[a][b]=val+days[b];//a->b长度为路径+隔离 
		graph[b][a]=val+days[a];
	}
	if(n==1)
	{//不加这个是AC不了的哦 
		cout<<0;
		return 0;
	}
	cout<<Dijkstra();

    return 0;
}

费用报销

动规

下面思路参考上面链接大佬的 ,对我来说很新奇的思路

dp[i][j]是前i天的发票尽可能填充j元的最大金额
    所以dp[i][j]=max(dp[i-1][j] 第i天的发票不选用  
                ,dp[i-k][j-money]+money 选用第i天发票 

#include<bits/stdc++.h>
using namespace std;
const int MAX=1e3+10;
typedef pair<int,int> PII;
typedef long long ll;
#define x first 
#define y second 
int dp[505][5005];
struct Node
{//每张发票 
	int money; //发票金额 
	int day;//发票日期是这一年第day天 
}invoices[MAX];
bool cmp(struct Node n1,struct Node n2)
{
	if(n1.day==n2.day)return n1.money<n2.money;
	else return n1.day<n2.day;
}
int n,m,k;
int months[]={0,31,28,31,30,31,30,31,31,30,31,30,31};
int main()
{
	int cnt=1;
	map<PII,int>mymap;//月:天 映射到这一年的第几天 
	for(int i=1;i<=12;i++)
	{//存储映射 
		for(int j=1;j<=months[i];j++)
		mymap[{i,j}]=cnt++;
	}
	cin>>n>>m>>k;
	for(int i=1;i<=n;i++)
	{
		int month,day,val;
		cin>>month>>day>>invoices[i].money;
		invoices[i].day=mymap[{month,day}];//发票日期是这一年第day天 
	}
	sort(invoices+1,invoices+n+1,cmp);//按日期递增排序
	   // for(int i=1;i<=n;i++)cout<<invoices[i].day<<" "<<invoices[i].money<<endl;

	/*dp[i][j]是前i天的发票尽可能填充j元的最大金额
	所以dp[i][j]=max(dp[i-1][j] 第i天的发票不选用  
				,dp[i-k][j-money]+money  选用第i天发票)
						
	*/
	int index=1; //标记invoices的下标 
	for(int i=1;i<=365;)
	{
		if(invoices[index].day==i)
		{//第i天有发票 
			int money=invoices[index].money;
			for(int j=money;j<=m;j++)
			{//把i天能装invoice[index]这张发票的都更新 
				dp[i][j]=max(dp[i-1][j],dp[max(0,i-k)][j-money]+money);
			}
			index++;//装下一个发票 但天数i不能变 因为第i天可能有多张发票 
		}
		else
		{//第i天没有支票 则第i天顺延前i-1天的钱数 
			for(int j=1;j<=m;j++)
			dp[i][j]=max(dp[i][j],dp[i-1][j]);
			i++;//第i天都没有支票了 i肯定要++ 
		}
	}
	cout<<dp[365][m]; //这里输出只要是365之后的天都一样 
    return 0;
}

故障

贝叶斯公式

明白题意:k是同时出现了k个故障现象,而不是k次查询

举例 :设B为样例说的3号故障现象(也就是只有3号发生,其他都不发生)

Ai为则第i故障原因导致 则p(A1|B)=(p(B|A1)*p(A1))/(和)

其中p(A1)=0.3(A1发生的概率)  , p(B|A1)=0.33*(1-0.5)*(1-0.25) (光3号发生,其他都不发生)

其他也是这种逻辑 

#include<bits/stdc++.h>
using namespace std;
const int MAX=1e2+10;
typedef pair<double,int> PII;
typedef long long ll;
#define x first 
#define y second 
double p[MAX];//Ai事件发生概率 
double pp[MAX][MAX];//Ai下j现象发生概率 
int occured[MAX];//标记j现象是否发生 
double numerator[MAX];//记录分母P(B|Ai)*p(Ai) 
bool cmp(PII p1, PII p2)
{
	if(p1.x==p2.x)return p1.y<p2.y;
	return p1.x>p2.x;
}
int main()
{
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	{
		cin>>p[i];
		p[i]/=100;
	}
	for(int i=1;i<=n;i++)
	{	
		for(int j=1;j<=m;j++)
		{
			cin>>pp[i][j];
			pp[i][j]/=100;
		}
	}

	int k,op;
	cin>>k;
	for(int i=1;i<=k;i++)
	{//标记op事件发生 
		cin>>op;
		occured[op]=1;
	}
	double sumB=0; //记录P(B) =sum(p(B|Ai)*p(i)) 
	for(int i=1;i<=n;i++)
	{//计算每个Ai的p(B|Ai)*p[Ai] 即发生的*pp[i][j] 不发生的* 1-pp[i][j]
		numerator[i]=1;
		for(int j=1;j<=m;j++)
		{
			if(occured[j])
			numerator[i]*=pp[i][j];
			else
			numerator[i]*=1-pp[i][j];
		}
		numerator[i]*=p[i];
		sumB+=numerator[i];
	}
	vector<PII>res;//{p,index}把结果放到res中,方便排序 
	res.resize(n+1);
	for(int i=1;i<=n;i++)
	{
		if(sumB==0)res[i]={0,i};//注意分母0情况 
		else res[i]={numerator[i]/sumB,i};
	}
	sort(res.begin()+1,res.end(),cmp);
	for(int i=1;i<=n;i++)
	printf("%d %.2f\n",res[i].y,res[i].x*100);//看题目输出格式 
    return 0;
}

 机房

LCA不会

 齿轮

找规律题-找出规律后很简单,但尽可能优化(放在最后肯定有他的道理)

相邻齿轮线速度相等 :线速度v=r*w 
b个齿轮r1,r2,r3,rn, v=r1*w1=r2*w2..=rn-1*wn-1.=rn*wn
上式可见 wn/w1=r1/rn=qi  ,结果与中间齿轮无关(!!!!) 
所以题目转化为是否半径r中有两个r比值为ri/rj=qi 

用set也超时,所以用倍增初始化

我再偷偷告诉你,这个题卡scanf

#include<bits/stdc++.h>
using namespace std;
const int MAX=2e6+10;
typedef pair<double,int> PII;
typedef long long ll;
#define x first 
#define y second 
int n,Q;
int r;
/*相邻齿轮线速度相等 :线速度v=r*w 
b个齿轮r1,r2,r3,rn, v=r1*w1=r2*w2..=rn-1*wn-1.=rn*wn
上式可见 wn/w1=r1/rn=qi  ,结果与中间齿轮无关(!!!!) 
所以题目转化为是否半径r中有两个r比值为ri/rj=qi 
*/
int myhash[MAX];//标记ri存在 
int exist[MAX];//标记qi情况存在 
signed main()
{
	scanf("%d%d",&n,&Q);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&r);
		if(myhash[r])exist[1]=1; //>1个ri,q1肯定存在 
		myhash[r]=1; 
	}
	
	for(int i=1;i<MAX;i++)
	{//判断r[i]的各个整数倍k,是否存在,存在则qk存在 
		if(!myhash[i])continue;
		for(int j=2*i;j<MAX;j+=i)
		{//j遍历r[i]的2...m倍 
			if(myhash[j])exist[j/i]=1; //j/i就是k 
		}
	}
	int q;
	for(int i=1;i<=Q;i++)
	{
		scanf("%d",&q);
		if(exist[q])cout<<"YES\n";
		else cout<<"NO\n"; 
	}
	return 0;
}

 搬砖

又是dp

(2条消息) 第十三届蓝桥杯国赛 C++ B 组 J 题——搬砖(AC)_执 梗的博客-CSDN博客

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值