编程题题目就不放了,各位到下面刷题链接看吧
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