[蓝桥杯解题报告]第六届蓝桥杯大赛省赛2015(软件类)真题C++A组 Apare_xzc

蓝桥杯第六届(2015年)省赛软件类C++A组解题报告

Apare_xzc 2020/3/12


在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


1. 方程整数解

在这里插入图片描述

分析:

        dfs即可。0^2 + 10^2 +30^2 = 1000

代码:

#include <bits/stdc++.h>
using namespace std;
int main() {
	for(int i=0; i<=40; ++i) 
		for(int j=i; j<=40; ++j) 
			for(int k=j; k<=40; ++k) 
				if(i*i+j*j+k*k==1000) {
					cout<<i<<" "<<j<<" "<<k<<endl;
					return 0; 
				}
	return 0;
}

这题我找到的解是0*0 + 10*10 + 30*30 = 1000,所以我的答案是0

在这里插入图片描述


2. 星系炸弹

在这里插入图片描述

分析:

        这个题可以直接数出来。到2014年11月30日,过了30 - 9 = 21(天),到2014年12月31日,过了21 + 31 = 52(天),那么到2016年12月31日,过了52 + 365 + 366 = 783(天),还有1000 - 783 = 217(天)。大概217天是7个月,2017年前7个月的天数之和为:31 + 28 + 31 + 30 + 31 + 30 + 31 = 212(天), 那么还有217 - 212 = 5(天),答案就是2017-08-05 。
        当然我们也可以写程序。写一个函数,给定当前日期和增加的天数,返回若干天以后的日期。代码如下。

代码:

#include <bits/stdc++.h>
using namespace std;
int md[] = {0,31,28,31,30,31,30,31,31,30,31,30,31};
bool Leap(int y) {
	if(y%400==0||y%4==0&&y%100) return 1;
	return 0;
}
int getMonthDay(int y,int m) {
	if(m!=2) return md[m];
	return md[m]+Leap(y);
}
void add(int &y,int &m,int &d,int add) {
	int monthday = getMonthDay(y,m),yday;
	if(d+add<=monthday) {
		d += add; return;
	}
	add -= monthday-d;
	d = monthday;
	for(int i=m+1; i<=12; ++i) {
		monthday = getMonthDay(y,i);
		if(add>=monthday) m = i, d = monthday, add -= monthday;
		else break;
	}
	if(add==0) return;
	if(m<12) {
		++m; d = add; return;
	}
	for(int i=y+1;; ++i) {
		yday = 365+Leap(i);
		if(add>=yday) add -= yday,++y;
		else break;
	}
	if(add==0) return;
	++y;
	if(add<=31) {
		m = 1; d = add; return;
	}
	for(int i=1; i<=12; ++i) {
		monthday = getMonthDay(y,i);
		if(add>=monthday) m = i, d = monthday, add-=monthday;
		else break;
	}
	if(add==0) return;
	if(m==12) m = 0;
	++m; d = add;
	return;
}
int main() {
	int y = 2014, m = 11, d = 9, i=1000;
	add(y,m,d,i);
	printf("%04d-%02d-%02d\n",y,m,d);
	return 0;
}

答案为:2017-08-05

在这里插入图片描述


3. 奇妙的数字

在这里插入图片描述

分析:

        因为立方在十位数之内,所以这个数并不大,暴力找就好了。

代码:

#include <bits/stdc++.h>
using namespace std;
bool f(int x) {
	int cnt[10] = {0};
	long long y = x*x;
	long long z = y*x;
	map<int,int> mp;
	while(y) cnt[y%10]++,y/=10;
	while(z) cnt[z%10]++,z/=10;
	bool ok = true;
	for(int i=0; i<10; ++i) {
		if(cnt[i]!=1) return false;
	}
	cout<<x<<endl;
	return true;
}
int main(void) {
	for(int i=1;; ++i) 
		if(f(i)) break;
	return 0;
}

答案为:69

在这里插入图片描述


4. 格子中输出

在这里插入图片描述

题目代码如下:

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

void StringInGrid(int width, int height, const char* s)
{
	int i,k;
	char buf[1000];
	strcpy(buf, s);
	if(strlen(s)>width-2) buf[width-2]=0;
	
	printf("+");
	for(i=0;i<width-2;i++) printf("-");
	printf("+\n");
	
	for(k=1; k<(height-1)/2;k++){
		printf("|");
		for(i=0;i<width-2;i++) printf(" ");
		printf("|\n");
	}
	
	printf("|");
	
	printf("%*s%s%*s",_____________________________________________);  //填空
	          
	printf("|\n");
	
	for(k=(height-1)/2+1; k<height-1; k++){
		printf("|");
		for(i=0;i<width-2;i++) printf(" ");
		printf("|\n");
	}	
	
	printf("+");
	for(i=0;i<width-2;i++) printf("-");
	printf("+\n");	
}

int main()
{
	StringInGrid(20,6,"abcd1234");
	return 0;
}

分析:

        题目要求就是居中显示一行字符串。%*s这个占位符需要两个参数,第一个参数是(unsigned) int类型的x,代表输出字符串应该站x个字符位置,第二个参数是字符串的头指针。
        我们知道,宽度为width,前后要有竖线,那么剩下width-2个位置,字符串的长度为strlen(s),所以左边和右边空格数为:(width-2-strlen(s))/2, 化简后为:(width - strlen(s)) / 2 - 1

答案为:(width-strlen(s))/2-1," ",buf,(width-strlen(s))/2-1," "

在这里插入图片描述


5. 9数组分组

在这里插入图片描述

题目代码如下:

#include <stdio.h>

void test(int x[])
{
	int a = x[0]*1000 + x[1]*100 + x[2]*10 + x[3];
	int b = x[4]*10000 + x[5]*1000 + x[6]*100 + x[7]*10 + x[8];
	
	if(a*3==b) printf("%d / %d\n", a, b);
}

void f(int x[], int k)
{
	int i,t;
	if(k>=9){
		test(x);
		return;
	}
	
	for(i=k; i<9; i++){
		{t=x[k]; x[k]=x[i]; x[i]=t;}
		f(x,k+1);
		_____________________________________________ // 填空处
	}
}
	
int main()
{
	int x[] = {1,2,3,4,5,6,7,8,9};
	f(x,0);	
	return 0;
}

分析:

        这是一个很好的通过交换生成全排列的做法,这样解决了递归可能的爆栈问题。
        从代码可以看出,每次将第x个数和后面的分别角换,生成新的排列。

答案:{t=x[k]; x[k]=x[i]; x[i]=t;}

在这里插入图片描述


6. 牌型种数

在这里插入图片描述

分析:

        52张没有大小王的牌中选13张,问有多少种不同的结果。每个点数的牌有4张。我们也不用容斥了,直接爆搜就好了。每个点数可以取0-4张,最后每个点数选的牌个数之和为13。

代码:

#include <bits/stdc++.h>
using namespace std;
long long ans = 0;
int r[13];
void dfs(int x,int sum) {
	if(x==13) {
		if(sum==13) ++ans; 
		return;
	}
	for(int i=0;i<=4;++i) {
		if(i+sum<=13) dfs(x+1,sum+i);
	}
}
int main(void) {
	dfs(0,0);
	cout<<ans<<endl;
	return 0;
} 

答案:3598180

在这里插入图片描述


7. 手链样式

在这里插入图片描述

分析:

        一看就是一个Polya定理。数字这么小,直接搜吧。dfs一个长度为12,由RWY中的字符组成的字符串,判断之前是否有等价的状态。我们知道,手链转动相当于字符串循环移位,手链翻转相当于字符串翻转。所以,没得到一个字符串,将它循环移位12次,加上翻转,24个状态都判断一遍,如果以前没有出现过这个状态,就计数。可以用map,也可以字符串哈希更快。

代码:

#include <bits/stdc++.h>
using namespace std;
char r[15] = "ABCDEFGHIJKL";
int ans = 0;
map<string,int> mp;
string toLeft(int d) {
	string s;
	for(int i=d;i<=d+11;++i)
	{
		s += r[i%12];
	}
	//cout<<s<<endl;
	return s;
}
void cal()
{
	r[12] = '\0';
	string str; 
	bool ok = true;
	for(int i=1;i<=12;++i)
	{
		str = toLeft(i);
		if(mp.count(str)) {
			ok = false;break;
		} 
		reverse(str.begin(),str.end());
		if(mp.count(str)) {
			ok = false;break;
		}
	}
	if(ok) {
		mp[r] = 1;++ans;
	}
}
void dfs(int x,int cr,int cw,int cy)
{
	if(x==12){
		cal();
		return;
	}
	if(cr>0) {
		r[x] = 'R';
		dfs(x+1,cr-1,cw,cy);
	} if(cw>0) {
		r[x] = 'W';
		dfs(x+1,cr,cw-1,cy);
	} if(cy>0) {
		r[x] = 'Y';
		dfs(x+1,cr,cw,cy-1);
	}
}
int main()
{
	dfs(0,3,4,5);
	cout<<ans<<endl;
	return 0;
} 

答案:1170

在这里插入图片描述


8. 饮料换购

在这里插入图片描述

分析:

         这个不能赊账,也不能借,和我们做的小学奥数题不同,但贴近实际规则。直接除以3,计数,直到小于3。

代码:

#include <bits/stdc++.h>
using namespace std;
int main() {
	int n;
	while(cin>>n) {
		int ans = n;
		int add = n;
		while(add>=3) {
			ans += add/3;
			add = add/3+add%3;
		}
		cout<<ans<<endl;
	}
	return 0;
}

在这里插入图片描述


9. 垒骰子

在这里插入图片描述
在这里插入图片描述

分析:

      每个骰子向上有6中选择,确定了向上的数字后,可以水平旋转,有4中选择。我们设dp[x][y]为垒x个骰子,且第x个骰子朝上的数字为y的个数。那么,我们就可以由dp[x-1][t]来向dp[x][y]递推。只要满足y对面的数字y‘和t不排斥即可。所以,递推的伪代码可以写出来:

for y in range(1,6+1):
	dp[1][y] = 1 #第一个骰子朝上的数字为y的情况都为1种
for x in range(2,n+1): #防止第x个骰子
	for y in range(1,6+1):
		for t in range(1,6+1):
				dp[x][y] += can[op(y)][t] * dp[x-1][t]
sum = 0
for y in range(1,6+1):
	sum += dp[n][y]
sum = sum * pow(4,n)

我们可以用系数矩阵来描述这个关系:

| dp[x][1] |           |1  1  1  1  1  1|     |dp[x-1][1]|
| dp[x][2] |           |1  1  1  1  1  1|     |dp[x-1][2]|
| dp[x][3] |     =     |1  1  1  1  1  1|  *  |dp[x-1][3]|
| dp[x][4] |           |1  0  1  1  1  1|     |dp[x-1][4]|
| dp[x][5] |           |0  1  1  1  1  1|     |dp[x-1][5]|
| dp[x][6] |           |1  1  1  1  1  1|     |dp[x-1][6]|

于是乎,我们求出了系数矩阵,就可以矩阵快速幂加速dp了。
对于一对排斥的数字x,y,不能贴着。就是说,如果下层的x朝上,那么上层的op(y)就不饿能朝上,所以can[op(y)][x] = 0, 同理,can[op(x)][y] = 0
我们知道,dp[1][1],dp[1][2], … dp[1][6]均为1,那么用系数矩阵(66)的n-1次方左乘这个dp[1]列向量(61) 的到的新的列向量(6*1)即为dp[n],我们将dp[n]的六个值相加,然后乘以4^n即可。注意答案对1E9+7取模。

代码:

#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int mod = 1e9+7;
LL a[6][6];
LL fast_pow(LL a,LL b)
{
	if(a==0) return 0;
	LL ans = 1;
	while(b) {
		if(b&1) ans = ans*a%mod;
		a = a*a%mod;
		b>>=1;
	}
	return ans;
}
void Mul(LL a[6][6],LL b[6][6]) {
	LL c[6][6] = {0};
	for(int i=0;i<6;++i)
		for(int j=0;j<6;++j)
			for(int k=0;k<6;++k)
				c[i][j] = (c[i][j]+a[i][k]*b[k][j]%mod)%mod;
	for(int i=0;i<6;++i)
		for(int j=0;j<6;++j)
			a[i][j] = c[i][j]; 
}
void Mat_fast_pow(LL a[6][6],int n) {
	LL p = fast_pow(4ll,n); --n;
	LL ans[6][6] = {0};
	for(int i=0;i<6;++i) ans[i][i] = 1;
	while(n) {
		if(n&1) Mul(ans,a);
		Mul(a,a);
		n>>=1;
	}
	LL res = 0;
	for(int i=0;i<6;++i)
		for(int j=0;j<6;++j)
			res = (res+ans[i][j])%mod;
	res = res * p % mod;
	cout<<res<<endl;
}
int op(int x) {
	return (x+3)%6;
} 
int main()
{
	int n,m,x,y;
	while(cin>>n>>m)
	{
		for(int i=0;i<6;++i)
			for(int j=0;j<6;++j)
				a[i][j] = 1;
		while(m--){
			cin>>x>>y; 
			--x,--y;
			a[op(x)][y] = a[op(y)][x] = 0;
		}
		Mat_fast_pow(a,n);
	}
	return 0;
}

在这里插入图片描述


10. 灾后重建

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

样例输入:

7 10 4
1 3 10
2 6 9
4 1 5
3 7 4
3 6 9
1 5 8
2 7 4
3 2 10
1 7 6
7 6 9
1 7 1 0
1 7 3 1
2 5 1 0
3 7 2 1

样例输出:

9
6
8
8

分析:

        就是说,一个图,所有的边都被破坏,现在要让其中的某些节点相互连通,让我们连一些边,要求最长的边最小化。求最长的边的长度。
        我们可以贪心地想,将边按长度(维修时间)从小大排序。然后从小到大连边,知道选出的所有点都互相连通就停止,那么最后连的那一条边即为答案。判断图的连通性可以用并查集。这也就是最小生成树的Kruskal算法的思想。
        由于并查集check每次初始化都要O(n), 我们不如二分答案。二分加几条边可以满足连通。
        注意有重边和自环,我们可以用map<pair<int,int>,int>处理输入的边

代码:

#include <bits/stdc++.h>
#define MP make_pair
#define pb push_back
using namespace std;
const int maxn = 5E4+10;
const int maxm = 2E5+10;
struct Node{
	int to,Next,d;
}node[maxm*2];
int head[maxn],tot;
void init(int n) {
	memset(head,-1,sizeof(head));
	tot = 0;
}
void addedge(int u,int v,int d) {
	node[tot].to = v;
	node[tot].d = d;
	node[tot].Next = head[u];
	head[u] = tot++;
}
struct E{
	int u,v,d;
	E(int _u=0,int _v=0,int _d=0):u(_u),v(_v),d(_d){} 
	bool operator < (const E& rhs)const {
		return d < rhs.d;
	}
}edge[maxm];
int pre[maxn];
int Find(int x) {
	return x==pre[x]?x:pre[x]=Find(pre[x]);
}
void join(int x,int y) {
	int fx = Find(x), fy = Find(y);
	if(fx==fy) return;
	pre[fy] = fx; 
}
int main()
{
	int m,n,q,x,y,p,L,R,k,c;
	scanf("%d%d%d",&n,&m,&q);
	init(n);
	map<pair<int,int>,int> mpe;
	map<pair<int,int>,int>::iterator it;
	pair<int,int> pr;
	for(int i=0;i<m;++i) {
		scanf("%d%d%d",&x,&y,&p);
		if(x==y) continue;
		if(x>y) swap(x,y);
		pr = make_pair(x,y);
		if(!mpe.count(pr)||mpe[pr]>p) mpe[pr] = p;
	} 
	m = 0;
	for(it=mpe.begin();it!=mpe.end();++it){
		pr = it->first; x = pr.first; y = pr.second;
		p = it->second;
		addedge(x,y,p);
		addedge(y,x,p);
		edge[++m] = E(x,y,p);
	} 
	sort(edge+1,edge+m+1);
	for(int ca=1;ca<=q;++ca) {
		scanf("%d%d%d%d",&L,&R,&k,&c);
		int fir = L;
		for(;fir<=R;++fir)
			if(fir%k==c) break;
		map<int,int> mp;
		vector<int> v;
		int sz = 0,fa;
		while(fir<=R)
			mp[fir] = 1,v.pb(fir),fir+=k, ++sz;
		int left = 0, right = m, mid;
		while(right-left>1) {
			mid = (left+right)>>1;
			for(int i=1;i<=n;++i) pre[i] = i;
			for(int i=1;i<=mid;++i) {
				join(edge[i].u,edge[i].v); 
			}
			fa = Find(v[0]);
			bool ok = true;
			for(int i=1;i<sz;++i) {
				if(Find(v[i])!=fa) {
					ok = false; break;
				}
			}
			if(ok) right = mid;
			else left = mid;
		}
		printf("%d\n",edge[right].d);
	}
	return 0;
}

2020.3.12
23:17
xzc


猜你喜欢

转载自blog.csdn.net/qq_40531479/article/details/104826083