蓝桥杯题选(C++ A组)(2019.2020.2021.2022省赛及2020国赛部分题)

目录

2021省赛

砝码称重

异或数列

2020省赛

七段码​编辑

子串分值

2019省赛

迷宫

外卖店优先级

修改数组

糖果

2020国赛

玩具蛇

答疑 

2022省赛

选数异或

爬树的甲壳虫 

2021省赛

砝码称重

砝码称重https://www.acwing.com/problem/content/3420

略有变形的01背包问题

先把所有砝码可能达到的最大总重sum计算出来,每次多讨论一个砝码时枚举1~sum之间的所有数,如果被标记过(表明之前已经得到过这个重量),我们可以在此数基础上加上当前砝码重量/减去当前砝码重量,并对得到的数值进行标记,并把当前讨论的砝码的单独的重量打上标记,这样一层一层推进。

#include<bits/stdc++.h>
using namespace std;
int n,a[110],ok[100010][110],sum;

int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
		sum+=a[i];
	}
	ok[0][0]=1;
	for(int i=1;i<=n;i++)
	{
		for(int j=0;j<=sum;j++)
		{
			if(ok[j][i-1])
			{
				ok[j+a[i]][i]=1;
				ok[abs(j-a[i])][i]=1;
				ok[j][i]=1;
			}
		}
		ok[a[i]][i]=1;
	}
	int ans=0;
	for(int i=1;i<=sum;i++)
		if(ok[i][n]) ans++;
	cout<<ans;
	return 0;
}

异或数列

异或数列https://www.acwing.com/problem/content/3424/

博弈问题

先判断一下每组数内的所有数是否都出现两次(异或和为0),此时为平局情况。

若不为平局,则要比谁最后可以拿到二进制下最高位的有效的1。

于是先遍历所有数,按二进制数位统计每个数位上1的个数——

  • 如果最高位的1有偶数个:无效,直接比较次最高位;

  • 如果最高位的1有奇数个:

    • 特判:如果最高位1只对应1个数,这个数一定会率先被拿走,所以是先手胜出;

    • 若n的总数为奇数个,先手先拿最高位是1的数,若后手拿最高位是0的数,先手就拿最高位是0的数,若后手拿最高位是1的数,先手就拿最高位是1的数,最后是先手胜出;

    • 若n的总数为偶数个,则含有奇数个最高位是0的数,后手有办法拿到最高位奇数个0和奇数个1,先手只能拿偶数个最高位1,后手胜出。

#include<bits/stdc++.h>
using namespace std;
int T;
int a[32];

int main()
{
	cin>>T;
	while(T--)
	{
		memset(a,0,sizeof(a));
		int n;
		cin>>n;
		int ans=0;
		for(int j=0;j<n;j++)
		{
			int x;
			cin>>x;
			ans^=x;
			for(int i=1;i<=30&&x;i++)
			{
				if(x&1) a[i]++;
				x>>=1;
			}
		}
		if(!ans) cout<<0<<endl;
		else
		{
			for(int i=30;i>0;i--)
			{
				if(a[i]==1)
				{
					cout<<1<<endl;
					break;
				}
				else if(a[i]&1)
				{
					if(n&1) cout<<1<<endl;
					else cout<<-1<<endl;
					break;
				}
			}
		} 
	}
	return 0;
}

2020省赛

七段码

并查集

//answer :80
#include<bits/stdc++.h>
using namespace std;
int mp[8][8],vis[8],fa[8],ans=0;

int find(int x)
{
	if(fa[x]==x) return x;
	else return fa[x]=find(fa[x]);
}

void dfs(int k)
{
	if(k>7)
	{
		for(int i=1;i<=7;i++) fa[i]=i;
		for(int i=1;i<=7;i++)
		{
			for(int j=1;j<=7;j++)
			{
				if(mp[i][j]&&vis[i]&&vis[j])
				{
					int x=find(i),y=find(j);
					if(x!=y) fa[x]=y;
				}
			}
		}
		int cnt=0;
		for(int i=1;i<=7;i++)
		{
			if(vis[i]&&fa[i]==i) cnt++;
		}
		if(cnt==1) ans++;
		return;
	}
	vis[k]=1;
	dfs(k+1);
	vis[k]=0;
	dfs(k+1);
}

int main()
{
	mp[1][2]=mp[2][1]=1;
	mp[1][6]=mp[6][1]=1;
	mp[2][3]=mp[3][2]=1;
	mp[2][7]=mp[7][2]=1;
	mp[3][4]=mp[4][3]=1;
	mp[3][7]=mp[7][3]=1;
	mp[4][5]=mp[5][4]=1;
	mp[5][6]=mp[6][5]=1;
	mp[5][7]=mp[7][5]=1;
	mp[6][7]=mp[7][6]=1;
	dfs(1);
	cout<<ans;
	return 0;
}

子串分值

子串分值https://www.acwing.com/problem/content/2871/

#include<std/c++.h>
using namespace std;
const int N=1e5+5;
int pre[N],next[N],a[27];
string s;
long long ans=0;

int main()
{
	cin>>s;
	int n=s.size();	
	s=' '+s;//让下标从1开始	 
	for(int i=1;i<=n;i++)
	{
		pre[i]=a[s[i]-'a'];
		a[s[i]-'a']=i;
	}//滚动一轮,得到pre数组 
	for(int i=0;i<26;i++)
		a[i]=n+1;//为了让next有初始的值(最后) 
	for(int i=n;i>0;i--)
	{
		next[i]=a[s[i]-'a'];
		a[s[i]-'a']=i;
	}//滚动一轮,得到next数组 
	for(int i=1;i<=n;i++)
		ans+=(i-pre[i])*(next[i]-i)
	//每个字符(设为ch)有一个贡献的区间 (上次出现ch的位置,下次出现ch的位置) 
	//从区间( pre[i],i)内任选一点, 从区间( next[i],i)内任选一点,s[i]在这样的两点构成的区间内只出现一次
	//对于s[i],符合条件的区间有 (i-pre[i])*(next[i]-i)个 
	cout<<ans; 
	return 0;
}

2019省赛

迷宫

01010101001011001001010110010110100100001000101010
00001000100000101010010000100000001001100110100101
01111011010010001000001101001011100011000000010000
01000000001010100011010000101000001010101011001011
00011111000000101000010010100010100000101100000000
11001000110101000010101100011010011010101011110111
00011011010101001001001010000001000101001110000000
10100000101000100110101010111110011000010000111010
00111000001010100001100010000001000101001100001001
11000110100001110010001001010101010101010001101000
00010000100100000101001010101110100010101010000101
11100100101001001000010000010101010100100100010100
00000010000000101011001111010001100000101010100011
10101010011100001000011000010110011110110100001000
10101010100001101010100101000010100000111011101001
10000000101100010000101100101101001011100000000100
10101001000000010100100001000100000100011110101001
00101001010101101001010100011010101101110000110101
11001010000100001100000010100101000001000111000010
00001000110000110101101000000100101001001000011101
10100101000101000000001110110010110101101010100001
00101000010000110101010000100010001001000100010101
10100001000110010001000010101001010101011111010010
00000100101000000110010100101001000001000000000010
11010000001001110111001001000011101001011011101000
00000110100010001000100000001000011101000000110011
10101000101000100010001111100010101001010000001000
10000010100101001010110000000100101010001011101000
00111100001000010000000110111000000001000000001011
10000001100111010111010001000110111010101101111000 

answer:DDDDRRURRRRRRDRRRRDDDLDDRDDDDDDDDDDDDRDDRRRURRUURRDDDDRDRRRRRRDRRURRDDDRRRRUURUUUUUUULULLUUUURRRRUULLLUUUULLUUULUURRURRURURRRDDRRRRRDDRRDDLLLDDRRDDRDDLDDDLLDDLLLDLDDDLDDRRRRRRRRRDDDDDDRR

典型的·BFS,值得关注的就是这种记录路径的办法。

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

struct position
{
	int x,y;
	string path;
};

int maze[55][55];
int vis[55][55];
int dx[4]={1,0,0,-1};
int dy[4]={0,-1,1,0};
char direction[4]={'D','L','R','U'};

vector<char>vec;
queue<position>q;

bool judge(position next)
{
	if(!maze[next.x][next.y]&&next.x>=1&&next.x<=30&&next.y>=1&&next.y<=50&&!vis[next.x][next.y])
		return true;
	return false;
}

void bfs()
{
	position start;
	start.x=1,start.y=1;
	q.push(start);
	while(q.size())
	{
		position now=q.front();
		q.pop();
		if(now.x==30&&now.y==50)
		{
			cout<<now.path;
			return;
		}
		vis[now.x][now.y]=1;
		for(int i=0;i<4;i++)
		{
			position next;
			next.x=now.x+dx[i];
			next.y=now.y+dy[i];
			next.path=now.path+direction[i];
			if(judge(next)) q.push(next);
		}
	}
}

int main()
{
	for(int i=1;i<=30;i++)
		for(int j=1;j<=50;j++)
			scanf("%1d",&maze[i][j]);
	bfs();
	return 0;
}

外卖店优先级

外卖店优先级https://www.acwing.com/problem/content/1243/

直接暴力肯定超时,我想着写O(m)的做法,结果错了六发。。。(腹诽:还不如暴力骗80分)要注意的小细节非常多。

大体的思路就是先把题给的数据存到对应编号的vec里,然后遍历这n家外卖店的vec,对于每家店的vec:

1.先把所有数据排序!(题目给的订单不是按照时间顺序排列的)

 sort(vec[i].begin(),vec[i].end());

2.第一个数据:除了加2个优先级之外啥也不干(在第一个订单出现之前优先级一直都是0)

3.从第二个数据(如果存在的话)一直到最后一个数据:每次先处理空档造成的优先级下降(从vec[i][j]到vec[i][j-1]有(vec[i][j]-vec[i][j-1]-1)个时间段),如果优先级下降到小于等于3,标记被清出优先缓存,又由于当前订单会使得优先级加2,如果优先级大于5了,标记进入优先缓存。

 if(vec[i][j]-vec[i][j-1]-1>0)
     priority=max(0,priority-(vec[i][j]-vec[i][j-1]-1));
 if(priority<=3) flag=0;
 priority+=2;
 if(priority>5) flag=1;

3.从最后一个数据到T个时刻过去:从最后一个数据到T之间还有(T-vec[i][vec[i].size()-1])个时间段,会使优先级下降,最后判断一下是否会下降到小于等于3即可

 priority=max(0,priority-(T-vec[i][vec[i].size()-1]));
 if(priority>3&&flag) cnt++;

全部代码:

#include<bits/stdc++.h>
using namespace std;
vector<int>vec[100001];
int N,M,T,cnt;

int main()
{
	cin>>N>>M>>T;
	while(M--)
	{
		int t,num;
		scanf("%d%d",&t,&num);
		vec[num].push_back(t);
	}
	for(int i=1;i<=N;i++)
	{
		if(vec[i].empty()) continue;
		sort(vec[i].begin(),vec[i].end());
		int priority=0,flag=0;
		for(int j=0;j<vec[i].size();j++)
		{
			//cout<<vec[i][j]<<' ';
			if(j==0)
			{
				priority+=2;
				continue;
			}
			if(vec[i][j]-vec[i][j-1]-1>0)
				priority=max(0,priority-(vec[i][j]-vec[i][j-1]-1));
			if(priority<=3) flag=0;
			priority+=2;
			//cout<<" priority:"<<priority<<' ';
			if(priority>5) flag=1;
		}
		priority=max(0,priority-(T-vec[i][vec[i].size()-1]));
		//cout<<"last:"<<priority<<endl;
		if(priority>3&&flag) cnt++;
	}
	cout<<cnt;
	return 0;
}

修改数组

修改数组https://www.acwing.com/problem/content/1244/

#include<bits/stdc++.h>
using namespace std;
int N;
int a[100001],fa[2000000];
int find(int x)
{
	if(fa[x]==x) return x;
	else return fa[x]=find(fa[x]);
}

int main()
{
	cin>>N;
	for(int i=1;i<=2000000;i++) fa[i]=i;
	for(int i=1;i<=N;i++)
	{
		scanf("%d",&a[i]);
		if(find(a[i])==a[i])
			fa[a[i]]++;
		else 
		{
		    a[i]=find(a[i]);
			fa[a[i]]++;
		}
		cout<<a[i]<<' ';
	}
	return 0;
} 

糖果

状态压缩+01背包

糖果https://www.acwing.com/problem/content/1245/

状态表示:dp[i][s]为从前i个糖果中选,选出来的糖果种类状态为s,所需最少糖果包数

阶段划分:类似于01背包问题中,从前i个物品中选

转移方程:划分依据:对于第i个物品选还是不选

 dp[i][s]=min(dp[i-1][s],dp[i-1][s^(s&w[i])]+1);
//              不选              选
//(s&w[i])表示当前糖果袋中和之前已有的重合的部分
​​​​​​​//s^(s&w[i])表示去掉重合的部分(未选之前的状态)

边界:dp初始化为0x3f3f3f3f ,dp[0][0];

目标:dp[n][(1<<m-1]

时间复杂度:O(n * 2^m)

二维数组(会爆):

#include<bits/stdc++.h>
using namespace std;
int n,m,k;
int dp[101][1<<10];
//当前枚举到第几个,哪些种的糖果已经被选中,最小的糖果包数 
int w[101];

int main()
{
	cin>>n>>m>>k;
	memset(dp,0x3f,sizeof(dp));
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=k;j++)
		{
			int num;
			cin>>num;
			w[i]=w[i]|(1<<(num-1));
		}
	}
	dp[0][0]=0;
	for(int i=1;i<=n;i++)
	{
		for(int s=0;s<1<<m;s++)
		{
			dp[i][s]=min(dp[i-1][s],dp[i-1][s^(s&w[i])]+1);

		}	
	}
	if(dp[n][(1<<m)-1]==0x3f3f3f3f) cout<<-1;
	else cout<<dp[n][(1<<m)-1];
	return 0;
}

滚动数组:

#include<bits/stdc++.h>
using namespace std;
int n,m,k;
int dp[2][1<<20]; 
int w[101];

int main()
{
	cin>>n>>m>>k;
	memset(dp,0x3f,sizeof(dp));
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=k;j++)
		{
			int num;
			cin>>num;
			w[i]=w[i]|(1<<(num-1));
		}
	}
	dp[0][0]=0;
	for(int i=1;i<=n;i++)
	{
		for(int s=0;s<1<<m;s++)
		{
			dp[i%2][s]=min(dp[(i-1)%2][s],dp[(i-1)%2][s^(s&w[i])]+1);
		}	
	}
	if(dp[n%2][(1<<m)-1]==0x3f3f3f3f) cout<<-1;
	else cout<<dp[n%2][(1<<m)-1];
	return 0;
}

一维数组:

#include<bits/stdc++.h>
using namespace std;
int n,m,k;
int dp[1<<20];
int w[101];

int main()
{
	cin>>n>>m>>k;
	memset(dp,0x3f,sizeof(dp));
	for(int i=1;i<=n;i++)
		for(int j=1;j<=k;j++)
		{
			int num;
			cin>>num;
			w[i]=w[i]|(1<<(num-1));
		}
	dp[0]=0;
	for(int i=1;i<=n;i++)
		for(int s=0;s<1<<m;s++)
			dp[s]=min(dp[s],dp[s^(s&w[i])]+1);
	if(dp[(1<<m)-1]==0x3f3f3f3f) cout<<-1;
	else cout<<dp[(1<<m)-1];
	return 0;
}

2020国赛

玩具蛇

【问题描述】
小蓝有一条玩具蛇,一共有 16 节,上面标着数字 1 至 16。每一节都是一个正方形的形状。相邻的两节可以成直线或者成 90 度角。

小蓝还有一个 4 × 4 的方格盒子,用于存放玩具蛇,盒子的方格上依次标着字母 A 到 P 共 16 个字母。

小蓝可以折叠自己的玩具蛇放到盒子里面。他发现,有很多种方案可以将玩具蛇放进去。

下图给出了两种方案:


请帮小蓝计算一下,总共有多少种不同的方案。如果两个方案中,存在玩具蛇的某一节放在了盒子的不同格子里,则认为是不同的方案。

【答案提交】
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。

//answer:552
#include<bits/stdc++.h>
using namespace std;
int dx[4]={1,0,-1,0};
int dy[4]={0,1,0,-1};
int vis[5][5];
int ans=0;

void dfs(int x,int y,int now)
{
	if(now==16)
	{
		ans++;
		return;
	}
	vis[x][y]=1;
	for(int i=0;i<4;i++)
	{
		int xx=x+dx[i];
		int yy=y+dy[i];
		if(!vis[xx][yy]&&xx>=1&&xx<=4&&yy>=1&&yy<=4)
			dfs(xx,yy,now+1); 
	}
	vis[x][y]=0;
	return;
}
int main()
{
	dfs(1,1,1);
	int temp1=ans;
	dfs(2,2,1);
	int temp2=ans-temp1;
	dfs(2,1,1);
	int temp3=ans-temp1-temp2;
	cout<<temp1*4+temp2*4+temp3*8;
	return 0;
}

答疑 

时间限制: 1.0s 内存限制: 256.0MB

【问题描述】
有 n 位同学同时找老师答疑。每位同学都预先估计了自己答疑的时间。老师可以安排答疑的顺序,同学们要依次进入老师办公室答疑。

一位同学答疑的过程如下:

首先进入办公室,编号为 i 的同学需要 si 毫秒的时间。
然后同学问问题老师解答,编号为 i 的同学需要 ai 毫秒的时间。
答疑完成后,同学很高兴,会在课程群里面发一条消息,需要的时间可 以忽略。
最后同学收拾东西离开办公室,需要 ei 毫秒的时间。一般需要 10 秒、20 秒或 30 秒,即 ei 取值为 10000,20000 或 30000。
一位同学离开办公室后,紧接着下一位同学就可以进入办公室了。
答疑从 0 时刻开始。老师想合理的安排答疑的顺序,使得同学们在课程群里面发消息的时刻之和最小。

【输入格式】
输入第一行包含一个整数 n,表示同学的数量。
接下来 n 行,描述每位同学的时间。其中第 i 行包含三个整数 si, ai, ei,意义如上所述。

【输出格式】
输出一个整数,表示同学们在课程群里面发消息的时刻之和最小是多少。

【样例输入】
3
10000 10000 10000
20000 50000 20000
30000 20000 30000

【样例输出】
280000

【样例说明】
按照 1, 3, 2 的顺序答疑,发消息的时间分别是 20000, 80000, 180000。

【评测用例规模与约定】
对于 30% 的评测用例,1 ≤ n ≤ 20。
对于 60% 的评测用例,1 ≤ n ≤ 200。
对于所有评测用例,1 ≤ n ≤ 1000,1 ≤ si ≤ 60000,1 ≤ ai ≤ 1000000,ei ∈ {10000, 20000, 30000},即 ei 一定是 10000、20000、30000 之一。

#include<bits/stdc++.h>
using namespace std;
int n;
struct student
{
	int s,a,e;
}stu[1001];
bool cmp(student x,student y)
{
	if((x.a+x.e+x.s)!=(y.a+y.e+y.s))
	return (x.a+x.e+x.s)<(y.a+y.e+y.s);
	else return (x.a+x.e)<(y.a+y.e);
}

int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
		scanf("%d%d%d",&stu[i].s,&stu[i].a,&stu[i].e);
	sort(stu+1,stu+n+1,cmp);
	long long sum;
	int time;
	for(int i=1;i<=n;i++)
	{
		time+=(stu[i].s+stu[i].a);
		sum+=time;
		time+=stu[i].e;
	}
	cout<<sum;
	return 0;
}

2022省赛

选数异或

#include<bits/stdc++.h> 
using namespace std;
const int N=10010;
int dp[N],a[N];
int n,m,x,l,r;
map<int,int>mem;
int main()
{
	cin>>n>>m>>x;
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		dp[i]=max(dp[i-1],mem[a[i]^x]);
		mem[a[i]]=i;
	}
	while(m--)
	{
		scanf("%d%d",&l,&r);
		if(dp[r]>=l) printf("yes\n");
		else printf("no\n");
	}
	return 0;
}

爬树的甲壳虫 

https://www.luogu.com.cn/problem/P8774​​​​​​​

递推式推导

#include<iostream>
using namespace std;
typedef long long LL;
const LL mod=998244353;
int n;
LL ans,x,y;

LL qmi(LL a,LL b)
{
	LL res=1;
	while(b)
	{
		if(b&1) res=res*a%mod;
		a=a*a%mod;
		b>>=1;
	}
	return res;
}
int main()
{
	cin>>n;
	while(n--)
	{
		scanf("%lld%lld",&x,&y);
		ans=(ans+1)*y%mod*qmi(y-x,mod-2)%mod;
		ans=(ans+mod)%mod;
	}
	cout<<ans;
	return 0;
}

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

春弦_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值