潍坊学院第四届程序设计竞赛题解

题目难度

简单:A、C、F、H、J
中等:B、E、G、I
困难:D

Problem A:做一个正气的潍院人

思路:从中间S3区域入手,S3区域每行有N个"WFU"相连,为了让S3区域是正方形,那么S3区域的行数是N的3倍,S2区域的行数也是N的3倍,S1区域的列数自然是N的5倍。(画图即可看出)

代码:C/C++

#include<bits/stdc++.h> 
using namespace std;
int main()
{
	char s[10]={'W','F','U'}; 
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n*5;j++)
			printf("X");
		printf("\n"); 
	}
	for(int i=1;i<=n*3;i++)
	{
		for(int j=1;j<=n;j++)
			printf("X");
		for(int g=1;g<=n;g++)
			printf("%s",s);
		for(int k=1;k<=n;k++)
			printf("X");
		printf("\n");
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n*5;j++)
			printf("X");
		printf("\n"); 
	}	
	return 0;
}

Problem B:无上至尊的提案

思路:8种情况,全排列a,b,c共6种,其中(abc,acb)、(bac,bca) 、(cba,cab)是三种相同的情况,
我们就看(abc,acb),abc和acb全部组合12种,其中有4种重复,有如下8种情况。
a + b * c;
a * b + c;
(a + b) * c;
a * (b + c);
a * b * c;
a + b + c;
a * c + b;
(a + c) * b;

8种情况取最大就是答案。

代码:C/C++

#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
ll cmp(int a,int b)
{
	return a>b;
 } 
int main()
{
	ll t;
	ll num[10];
	cin>>t;
	while(t--)
	{
		ll a,b,c;
		cin>>a>>b>>c;
		num[0]=a+b*c;
		num[1]=a*b+c;
		num[2]=(a+b)*c;
		num[3]=a*(b+c);
		num[4]=a*b*c;
		num[5]=a+b+c;
		num[6]=a*c+b;
		num[7]=(a+c)*b;
		sort(num,num+8,cmp);
		cout<<num[0]<<endl;
	}
	return 0;
 } 

Problem C:魔王逗勇者

思路:从大到小排序,输出第m个数-1。

#include <iostream>
#include <algorithm>
#include <cmath>
#include <vector>
#include <string>
#include <cstring>
#include <stack>
using namespace std;
typedef long long ll;
struct stu{
	int atk;
}x[11000];
bool cmp(stu a,stu b){
	return a.atk>b.atk;
}
int main()
{
	int n,m;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++){
		scanf("%d",&x[i].atk);
	}
	sort(x+1,x+1+n,cmp);
	printf("%d\n",x[m].atk-1);
	return 0;
}

Problem D:树的扩充

题意:给你一个N个节点的树,增加若干条边形成完全图(每对不同的顶点之间都恰连有一条边相连),并满足形成的完全图所构成的最小生成树还是一开始给你的N各节点的树。

关键:如何处理加边

思路:最小生成树有克鲁斯卡尔算法和普利姆算法,分析两种算法思想:克鲁斯卡尔算法加边,普利姆算法加点,本题要处理边的问题,我们选取克鲁斯卡尔算法。

构造最小生成树,我们需要依次合并两个集合,克鲁斯卡尔算法是选择升序排序的边依次添加,为了保证我们增加若干条边后,新图的最小生成树还是开始的树,我们必须满足合并两个集合时这条边一定是这两个集合任意两点之间最小的,同时满足加的若干条边的权值和最小,那么这两个集合增加的若干条边的权值和为:(第一个集合的点 * 第二个集合的点 - 1)* (最小的边+1)

就是一个克鲁斯卡尔算法板子,多维护了一个S数组: S[x]代表x集合所包含的点的数量。

#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=6010;
ll f[maxn],s[maxn],ans,n;
struct node{
	ll s,e,num;
}edge[maxn];
ll cmp(node a, node b)
{
	return a.num<b.num;
}
void init(ll n)
{
	for(int i=0;i<=n;i++)
	{
		f[i]=i,s[i]=1;
	}
}
ll getf(ll x)
{
	if(x==f[x])
		return x;
	return f[x]=getf(f[x]);
}
void merge(ll x,ll y,ll z)
{
	ll tx=getf(x);
	ll ty=getf(y);
	if(tx!=ty)
	{
		ans+=(z+1)*(s[tx]*s[ty]-1);
		f[tx]=ty;
		s[ty]+=s[tx];
	}
}
int main()
{
	ll t;
	cin>>t;
	while(t--)
	{
		cin>>n;
		init(n);
		ans=0;
		for(int i=1;i<n;i++)
			cin>>edge[i].s>>edge[i].e>>edge[i].num;
		sort(edge+1,edge+n,cmp);
		for(int i=1;i<n;i++)
		{
			merge(edge[i].s,edge[i].e,edge[i].num);
		} 
		cout<<ans<<endl;
	}
	return 0;
 } 

Problem E:YES or NO

思路:把n转化成二进制,我们只需要统计这个二进制里面0的个数,如果0的个数不少于6个,输出yes,否则输出no。
分析:一个数能被64整除的话,那么这个数一定是64的倍数 ——> 转化成二进制就是从右边数第7位数一定是1,右面6个0(比如 101010101000000),这个数一定是64的倍数。
把一个数转化成二进制最高位一定是1,所以我们可以把最高位后面所有1去掉,就只剩下10000…,只需要统计一下0的个数就可以判断。

#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
ll a[100];
int main()
{
	ll n,k=0,flag=0;
	cin>>n;
	while(n)
	{
		a[k++]=n%2;
		n/=2;
	}
	ll num=0;
	for(int i=0;i<k;i++)
	{
		if(a[i]==0)
			num++;
		if(num==6)
		{
			flag=1;
			break;
		}
	}
	if(flag)
		cout<<"yes"<<endl;
	else
		cout<<"no"<<endl; 
	return 0;
 } 

Problem F:小迪的欢乐牧场

思路:数据范围不大,直接暴力求出N!,然后再(模10,除以10)循环k次就能求出第k位。

#include <bits/stdc++.h>
using namespace std;
int main()
{
    int t;
    scanf("%d", &t);
    while(t--) {
        int n, k;
        scanf("%d%d", &n, &k);
        int s = 1;
        for(int i = 1; i <= n; i++){
            s *= i;
        }
        int ans;
        for(int i = 1; i <= k; i++) {
            ans = s % 10;
            s /= 10;
        }
        printf("%d\n",ans);
    }
    return 0;
}

Problem G:Ak爷兼职计

思路:注意读题,每个数的位数不超过10,不是数不超过10,字符串比较大小 : a+b>b+a(a+b的字典序>b+a的字典序)。

#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=1e5+10;
string s[maxn];
int cmp(string a,string b)
{
	return a+b>b+a;
}
int main()
{
	int n;
	cin>>n;
	for(int i=0;i<n;i++)
		cin>>s[i];
	sort(s,s+n,cmp);
	for(int i=0;i<n;i++)
		cout<<s[i];
	cout<<endl;
	return 0;
 } 

Problem H:奥力给

恭喜你,签到成功,奥力给。
在这里插入图片描述

代码:C/C++

#include<iostream>
using namespace std;
int main()
{
	int n,m,p;
	cin>>n>>m>>p;
	cout<<(n+m)*p<<endl;
	return 0;
} 

Problem I:Bob的难题

思路:先统计’a’的个数,如果剩下的字符串的个数不能被2整除,S肯定找不到。
然后我们把字符串S求出(for循环 i :0 ~ a的个数+(T的长度-a的个数)/2 )
遍历字符串S,求出S′(把S中的‘a’去掉就是S′)
最后判断S+S′与T是否相等。

#include<iostream>
#include<cstring>
using namespace std;
int main()
{
	string t,s,s1;
	int cnt=0,len=0;
	cin>>t;
	len=t.size();
	for(int i=0;i<len;i++)
	{
		cnt+=(t[i]=='a');
	}
	if((len-cnt)%2!=0)	
		cout<<":("<<endl;
	else
	{
		for(int i=0;i<cnt+(len-cnt)/2;i++)//s
			s+=t[i];
		for(int i=0;i<cnt+(len-cnt)/2;i++)//s1
		{
			if(t[i]!='a')
				s1+=t[i];
		}
		cout<<((s+s1==t)?s:":(")<<endl;
		
	}
	return 0;
 } 

Problem J:XD爷的简单题

分情况讨论。注意:scanf的%c会读入空格。

#include <iostream>
#include <algorithm>
#include <cmath>
#include <vector>
#include <string>
#include <cstring>
#include <stack>
using namespace std;
typedef long long ll;

int main()
{
	int n,m;
	char ch;
	double ans;
	scanf("%d%d",&n,&m);
	getchar();
	scanf("%c",&ch);
	if(ch=='+')
	 ans=m+n;
	else if(ch=='*')
		ans=n*m;
	else if(ch=='/')
		ans=n*1.0/m;
	else if(ch='-')
		ans=n-m;
	printf("%.2lf\n",ans); 
	return 0;
}
发布了88 篇原创文章 · 获赞 30 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_43667611/article/details/103438098