再水一篇2017蓝桥杯C/C++省赛B组的题解

本文精选了10道算法竞赛题目,涵盖了模拟、数论、动态规划、深度优先搜索、二分查找、前缀和等多种算法类型,每道题都提供了详细的解题思路和参考代码,适合算法学习者和竞赛选手参考。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


编程题代码仅供参考,没跑过原题数据。
编程题代码仅供参考,没跑过原题数据。
编程题代码仅供参考,没跑过原题数据。



T1_购物单(模拟)

——题目描述——
在这里插入图片描述
——解——
这种题目是真的烦。
答案:5200



——Code——

#include<stdio.h>
double a[10000][2]={
180.90,       0.88,
10.25,       0.65,
56.14,        0.9,
104.65,        0.9,
100.30,      0.88,
297.15,       0.5,
26.75,       0.65,
130.62,       0.5,
240.28,       0.58,
270.62,        0.8,
115.87,       0.88,
247.34,       0.95,
73.21,        0.9,
101.00,       0.5,
79.54,       0.5,
278.44,        0.7,
199.26,       0.5,
12.97,        0.9,
166.30,       0.78,
125.50,       0.58,
84.98,        0.9,
113.35,       0.68,
166.57,       0.5,
42.56,        0.9,
81.90,       0.95,
131.78,        0.8,
255.89,       0.78,
109.17,        0.9,
146.69,       0.68,
139.33,       0.65,
141.16,       0.78,
154.74,        0.8,
59.42,        0.8,
85.44,       0.68,
293.70,       0.88,
261.79,       0.65,
11.30,       0.88,
268.27,       0.58,
128.29,       0.88,
251.03,        0.8,
208.39,       0.75,
128.88,       0.75,
62.06,        0.9,
225.87,       0.75,
12.89,       0.75,
34.28,       0.75,
62.16,       0.58,
129.12,       0.5,
218.37,       0.5,
289.69,       0.8,
};
int main()
{
	double ans=0;
	for(int i=0;i<10000;++i)
		ans+=a[i][0]*a[i][1];
	printf("%lf\n",ans);
	return 0;
}




T2_等差素数列(欧拉筛暴搜)

——题目描述——
在这里插入图片描述
——解——
本题考察的知识点为素数筛,从用欧拉筛打出1e7以内的素数,然后枚举公差,如果没答案,适当扩大枚举范围。
答案:210



——Code——

#include<iostream>
using namespace std;
bool num[10000007];
int prime[1000006];
int  Euler()
{
	int cnt=0;
	for(int i=2;i<=10000000;++i)
	{
		if(!num[i]) prime[cnt++]=i;
		for(int j=0;j<cnt&&prime[j]*i<=10000000;++j)
		{
			num[i*prime[j]]=true;
			if(!(i%prime[j])) break;
		}
	}
	return cnt;
}
int main()
{
	int len=Euler();
//	for(int i=0;i<len;++i)
//		cout<<prime[i]<<endl;
	for(int d=1;d<=1000;++d)
	{
		for(int i=0;i<len-10;++i)
		{
			int j;
			bool flag=false;
			for(j=1;j<=9;++j)
				if(prime[i]+j*d>=10000000||num[prime[i]+j*d])
				{
					flag=true;
					break;
				}	
			if(!flag) cout<<d<<" "<<prime[i]<<endl;
		}
	}
	return 0;
}




T3_承压计算(模拟)

——题目描述——

在这里插入图片描述
——解——
依旧是一道模拟题,从上往下递推,设某处为第i行第j列,此处的承压 G [ i ] [ j ] = G [ i − 1 ] [ j ] + G [ i − 1 ] [ j − 1 ] G[i][j]=G[i-1][j]+G[i-1][j-1] G[i][j]=G[i1][j]+G[i1][j1]
答案恰好是个整数:72665192664



——Code——

#include<iostream>
using namespace std;
double a[31][31];
int main()
{
	for(int i=1;i<=29;++i)
		for(int j=1;j<=i;++j)
			cin>>a[i][j];
	for(int i=1;i<=30;++i)
		for(int j=1;j<=i;++j)
			a[i][j]+=a[i-1][j]/2+a[i-1][j-1]/2;
	double min30=999999,max30=-999999;
	for(int i=1;i<=30;++i)
	{
		min30=min(a[30][i],min30);
		max30=max(a[30][i],max30);
	}
	printf("%.0lf\n",max30*(static_cast<double>(2086458231)/min30));
	return 0;
}




T4_方块分割(DFS)

——题目描述——
在这里插入图片描述
在这里插入图片描述
——解——
典型的深度优先搜索题,首先把方格坐标化,设最左下角点为 ( 0 , 0 ) (0,0) (0,0),对方格之间的线进行搜索,从 ( 3 , 3 ) (3,3) (3,3)开始,模拟中心对称图案线的性质(从起点延伸出两条中心对称的线),当线走到坐标边缘视作得出一种方案。当然,对于起点 ( 3 , 3 ) (3,3) (3,3),线有四个相同的方向可以延伸,所以会得出四个一样的方案,最终答案数需要除以4。
答案:509



——Code——

#include<iostream>
using namespace std;
int ans;
int dx[4]={1,0,-1,0};
int dy[4]={0,1,0,-1};
bool vis[7][7];
void dfs(int x,int y)
{
	if(x==0||x==6||y==0||y==6)
	{
		++ans;
		return;
	}
	for(int i=0;i<4;++i)
	{
		int X=x+dx[i];
		int Y=y+dy[i];
		if(X>=0&&X<=6&&Y>=0&&Y<=6&&vis[X][Y]==false)
		{
			vis[X][Y]=true;
			vis[6-X][6-Y]=true;
			dfs(X,Y);
			vis[X][Y]=false;
			vis[6-X][6-Y]=false;
		}
	}
}
int main()
{
	vis[3][3]=true;
	dfs(3,3);
	cout<<ans/4<<endl;
} 




T5_取位数(简单数学)

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

// 求x用10进制表示时的数位长度 
int len(int x){
    if(x<10) return 1;
    return len(x/10)+1;
}

// 取x的第k位数字
int f(int x, int k){
    if(len(x)-k==0) return x%10;
    return _____________________;  //填空
}

int main()
{
    int x = 23574;
    printf("%d\n", f(x,3));
    return 0;
}

在这里插入图片描述
——解——
可能是这10道题中写起来第二快的题,本题是典型的递归题,结合简单数学知识。当数字的长度等于k时,那么它的第k位数字就是该数的个位。如果长度大于k,那么就去掉末位,再进行比较。
答案: f ( x / 10 , k ) f(x/10,k) f(x/10,k)



T6_最大公共子串(动态规划)

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

#include <stdio.h>
#include <string.h>

#define N 256
int f(const char* s1, const char* s2)
{
    int a[N][N];
    int len1 = strlen(s1);
    int len2 = strlen(s2);
    int i,j;

    memset(a,0,sizeof(int)*N*N);
    int max = 0;
    for(i=1; i<=len1; i++){
        for(j=1; j<=len2; j++){
            if(s1[i-1]==s2[j-1]) {
                a[i][j] = __________________________;  //填空
                if(a[i][j] > max) max = a[i][j];
            }
        }
    }

    return max;
}

int main()
{
    printf("%d\n", f("abcdkkk", "baabcdadabc"));
    return 0;
}

——解——
这10道题中做起来最快的题,前提是知道动态规划。 a [ i ] [ j ] a[i][j] a[i][j]表达的是当s1遍历到第i位且s2遍历到第j位时各往前递推,子串的长度。如果这两个位置的字符刚好相等,那么此处的值就是上一位子串长度加1,否则就是0.
答案: a [ i − 1 ] [ j − 1 ] + 1 a[i-1][j-1]+1 a[i1][j1]+1



T7_日期问题(???模拟)

——题目描述——
在这里插入图片描述
——解——
三种情况套进去都检查一下就好了。
(一开始没看到只有三种排列,代码冗余了。。。)


#include<iostream>
#include<algorithm>
using namespace std;
int num[4];
int check(int y,int m)
{
	int flag=0;
	if(y%400==0||y%4==0&&y%100!=0)
		flag=1;
	switch(m)
	{
		case 1:
		case 3:
		case 5:
		case 7:
		case 8:
		case 10:
		case 12:return 31;
		case 4:
		case 6:
		case 9:
		case 11:return 30;
		default:break;
	}
	return 28+flag;
	
}
int main()
{
	char in[9];
	for(int i=1;i<=8;++i)
		cin>>in[i];
	num[1]=(in[1]-'0')*10+in[2]-'0';
	num[2]=(in[4]-'0')*10+in[5]-'0';
	num[3]=(in[7]-'0')*10+in[8]-'0';
	sort(num+1,num+4);
	for(int i=1;i<=3;++i)
	{
		if(i==2) continue;
		if(num[i]<60)
		{
			for(int j=1;j<=3;++j)
			{
				if(i==1&&j!=2||j==i)continue;
				else
				{
					int k=6-i-j;
					if(num[j]<=12&&num[k]<=check(num[i]+2000,num[j]))
					{
						cout<<num[i]+2000<<"-";
						if(num[j]<10) cout<<"0"<<num[j]<<"-";
						else cout<<num[j]<<"-";
						if(num[k]<10) cout<<"0"<<num[k]<<endl;
						else cout<<num[k]<<endl;
					}
				}
			}
		}
		else
		{
			for(int j=1;j<=3;++j)
			{
				if(j==i) continue;
				else
				{
					int k=6-i-j;
					if(num[j]<=12&&num[k]<=check(num[i]+1900,num[j]))
					{
						cout<<num[i]+1900<<"-";
						if(num[j]<10) cout<<"0"<<num[j]<<"-";
						else cout<<num[j]<<"-";
						if(num[k]<10) cout<<"0"<<num[k]<<endl;
						else cout<<num[k]<<endl;
					}
				}
			}
		}
	}
	return 0;
}




T8_包子凑数(数论)

——题目描述——
在这里插入图片描述
——解——
本题是一道数论题
先看无限多个的情况,比如样例4,6只能凑出偶数,在比如3,12,48只能凑出3的倍数,通过不完全归纳,可以得出结论:只有当所有数字的最大公约数不为1,凑不出的情况为有限个。
当确定答案为有限个时,接下来要做的就是粗略确定答案的上限。Ai最大为100,两个最大且互质的数为99和100,那么9900以后的整数一定都能被这两个数表示,证明过程可以参考我之前博客中的T4
现在可以延伸一下这个结论,对于任意两个互质的正整数a和b,大于等于ab的数都能被表示为ax+by,其中x和y均为正整数。
为了不浪费时间去找数组A中乘积最小的一对数字,可直接采用9900作为上限。
然后,用筛子把能访问到的数筛掉,顺便统计答案。



——Code——

#include<iostream>
using namespace std;
int n;
int a[101];
bool vis[9901];
int gcd(int a,int b)
{
	int r;
	do
	{
		r=a%b;
		a=b;
		b=r;
	}while(r!=0);
	return a;
}
int  search()
{
	int ans=0;
	for(int i=1;i<=9900;++i)
	{
		if(!vis[i])
		{
			++ans;
			continue;
		}
		for(int j=1;j<=n;++j)
			vis[i+a[j]]=true;
	}
	return ans;
}
int main()
{
	cin>>n;
	bool flag=true;
	int r;
	for(int i=1;i<=n;++i)
	{
		cin>>a[i];
		vis[a[i]]=true;
		if(flag)
		{
			r=a[i];
			flag=false;
		}
		r=gcd(a[i],r); 
	}
	if(r!=1) cout<<"INF\n"<<endl;
	else cout<<search()<<endl;
	return 0;
}




T9_分巧克力(二分)

——题目描述——
在这里插入图片描述
——解——
本题是一道典型的二分题,二分区间的可行域为左侧,区间的下限为1,上限为所有巧克力中的最长边。
检查枚举的边长是否符合要求也比较简单,只要把所有巧克力能裁剪出的数量累加即可。



——Code——

#include<iostream>
#include<algorithm>
using namespace std;
int n,k;
int a[2][100005];
bool check(int len)
{
	int cnt=0;
	for(int i=1;i<=n;++i)
		cnt+=(a[0][i]/len)*(a[1][i]/len);
	if(cnt>=k) return true;
	return false;
}
int main()
{
	cin>>n>>k;
	int maxl=-1;
	for(int i=1;i<=n;++i)
	{
		cin>>a[0][i]>>a[1][i];
		maxl=max(maxl,max(a[0][i],a[1][i])); 
	}
	int left=1,right=maxl;
	int ans=0;
	while(left<=right)
	{
		int mid=(left+right)/2;
		if(check(mid))
		{
			left=mid+1;
			ans=mid;
		}
		else right=mid-1;
	}
	cout<<ans<<endl;
	return 0;
}




T10_K倍数列(前缀和)

——题目描述——
在这里插入图片描述
——解——
还是比较水的一题,主要考察前缀和
在读入数据时存储从该数据开始以及之前所有数据的和,并且对 k k k取模。并对取模的结果进行统计,如果为0,则答案数直接加1。在没有取模之前, i &lt; j i&lt;j i<j时,必定有 s u m [ i ] &lt; s u m [ j ] sum[i]&lt;sum[j] sum[i]<sum[j],而 i 到 j i到j ij这一段区间的和即为 s u m [ j ] − s u m [ i ] sum[j]-sum[i] sum[j]sum[i],所以取模后,如果存在两个不等的下标 i 和 j i和j ij s u m [ j ] − s u m [ i ] = 0 sum[j]-sum[i]=0 sum[j]sum[i]=0,那么从 i 到 j i到j ij的和是 k k k的倍数,因此任意两个相等的 s u m sum sum之间能产生一个答案。累计 0 到 k − 1 0到k-1 0k1每个数出现的此时,分别套用组合数公式,累加起来。最后加上之前统计的0的个数,记为最终答案。



——Code——

#include<iostream>
#include<cstring>
using namespace std;
int sum[100005];
int cnt[100005];
int n,k;
int main()
{
	long long ans=0;
	cin>>n>>k;
	memset(cnt,-1,sizeof(cnt));
	for(int i=1;i<=n;++i)
	{
		int num;
		cin>>num;
		sum[i]=(num%k+sum[i-1])%k;
		++cnt[sum[i]];
		ans+=cnt[sum[i]];
		if(!sum[i]) ++ans;
	}
	cout<<ans<<endl;
	return 0;
} 

把开头的话再重复一下:
编程题代码仅供参考,没跑过原题数据。
编程题代码仅供参考,没跑过原题数据。
编程题代码仅供参考,没跑过原题数据。
上一篇题解也是这样的(学校的锅…)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值