河海大学第二十一届现场编程大赛-普及组 初赛题解
如果觉得还有需要,你仍然可以连接我们的校园热点HHUC并登录10.11.12.75,在题库中能看到本次比赛的赛题,并可进行提交。
文章目录
题目A 回火星(Mars)
题目大意
在若干个数字中找到一个最小的满足大于所输入的m两倍的值的位置。
大致思路
通过循环读入所给出的所有数字,对每一个数字与基准数字m*2进行比较,查看是否满足要求,满足要求则记录该数字位置。
需要注意理解题目中“往返”的需要。
参考代码
#include <cstdio>
#include <cstdlib>
using namespace std;
int n,m,x,wz;
int _min=0x7fffffff;
int main()
{
scanf("%d%d",&n,&m);
m*=2;
for(int i=1;i<=n;i++)
{
scanf("%d",&x);
if(x>=m&&x<_min)
{
_min=x;
wz=i;
}
}
if(_min==0x7fffffff)printf("No Answer!\n");
else printf("%d\n",wz);
return 0;
}
题目B 密码(Password)
题目大意
给出密钥,还原字母循环加密的字符串,需要注意字母大小写均需要进行还原。
大致思路
已知字母一共有26个因此,如如密钥后对其进行对26求余即可。
读入密钥后通过不断读入字符(或字符串),判断是否为字母,若为字母,需要对其进行解密处理,即减去密钥值,如果减完之后不处于字母范围则需要将这个值另外加上26达到循环的效果。
另:在宣讲会上已经提及过相关的读入直到所有数据读入完毕的操作,例如C语言中的while(scanf(…)!=EOF)以及C++语言中的cin>>…。因此在比赛中进行的相关询问均未做解答。
参考代码
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
using namespace std;
int main()
{
int key;
char s[10000];
char ans[100010];
memset(ans,0,sizeof(ans));
int len=-1;
scanf("%d",&key);gets(s);
key%=26;
while(scanf("%s",s)!=EOF)
{
for(int i=0;i< strlen(s);i++)
{
if(s[i]>='a'&&s[i]<='z')
{
s[i]-=key;
if(s[i]<'a')s[i]+=26;
}
if(s[i]>='A'&&s[i]<='Z')
{
s[i]-=key;
if(s[i]<'A')s[i]+=26;
}
ans[++len]=s[i];
if(len>=100000)
{
printf("%s",ans);
len=-1;
memset(ans,0,sizeof(ans));
}
}
}
printf("%s",ans);
return 0;
}
题目C 飞行路径(Path)
题目大意
连续给出10组点对,求他们的一次函数表达式(如除不尽用分数形式表达)。
大致思路
建立二元一次方程组,做除法时进行判定是否除得尽即可。如果除不尽的话可以使用辗转相除法求两数的最大公约数,分子分母同除以最大公约数即可获得最简分数。
注意需要重复进行10次计算。
参考代码
#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <algorithm>
using namespace std;
long long X1,X2,Y1,Y2;
long long k1,k2;
bool fs;
long long gcd(long long a,long long b)
{
long long r;
while(b>0)
{
r=a%b;
a=b;
b=r;
}
return a;
}
long long abs_(long long x)
{
if(x>0)return x;else return -x;
}
int main()
{
while(cin>>X1>>Y1>>X2>>Y2)
{
if(X1==X2||Y1==Y2)
{
printf("Error\n");
continue;
}
if(X1!=0)
{
swap(X1,X2);
swap(Y1,Y2);
}
k1=Y1; k2=X2;
if((k1<0&&k2<0)||(k1>0&&k2>0))fs=1;else fs=0;
k1=abs_(k1); k2=abs_(k2);
long long ggg=gcd(k1,k2);
k1/=ggg; k2/=ggg;
printf("y=");
if(fs)printf("-");
if((k1==1&&k2!=1)||(k1!=1))cout<<k1;
if(k2!=1)cout<<"/"<<k2;
printf("x");
if(Y1==0)printf("\n");
else
if(Y1<0)cout<<Y1<<"\n";else cout<<"+"<<Y1<<"\n";
}
return 0;
}
题目D 框星星(Star)
题目大意
找到一个最小的与坐标轴平行的正方形。
大致思路
分别在X,Y上找到最小值与最大值。(X_max-X_min)与(Y_max-Y_min)进行比较,较大者作为边长。然后得出正方形面积。
注意:考虑到
1
≤
x
,
y
≤
1
0
5
1≤x,y≤10^5
1≤x,y≤105,面积最大可能到达大约
1
0
10
10^{10}
1010,int类型无法正确计算及存储,应使用long long等。
参考代码
#include <cstdio>
#include <cstdlib>
using namespace std;
int n;
int x,y;
int max_h,max_s;
int min_h,min_s;
#define max_(x,y) ((x)>(y)?(x):(y))
int main()
{
scanf("%d",&n);
scanf("%d%d",&x,&y);
max_h=x; min_h=x;
max_s=y; min_s=y;
for(int i=1;i<n;++i)
{
scanf("%d%d",&x,&y);
if(x>max_h)max_h=x;
if(x<min_h)min_h=x;
if(y>max_s)max_s=y;
if(y<min_s)min_s=y;
}
long long ans=max_(max_h-min_h,max_s-min_s);
printf("%lld",ans*ans);
return 0;
}
题目E 停飞船(Parking)
题目大意
题目涉及到区间查询(S)、区间修改(I/O)两种操作。即需要根据题目指令对区间进行不断的操作与查询。
大致思路
该题对初步接触算法的同学们来说略有难度。观察数据范围1≤n≤10000,可知题目应当对每一次操作的时间复杂度在O(logn)或更低比较合适。因此可以选用树状数据结构。接下来的程序中使用的是线段树用以处理区间的修改与查询。
线段树的基本思路是对每一层,将其均分类两块作为下一层的左右节点进行分别查询,从而建立一棵以区间为节点的树,达到logn级别的操作时间复杂度。
参考代码
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <algorithm>
using namespace std;
struct node
{
int l,r,mid;
int num;
}tree[600010];
int n,t;
int x,y,num;
char ml;
long long ans;
void build(int xx,int yy,int tt)
{
tree[tt].l=xx; tree[tt].r=yy; tree[tt].mid=(xx+yy)>>1;
if(xx==yy)
{
scanf("%d",&tree[tt].num);
return;
}
build(xx,tree[tt].mid,(tt<<1));
build(tree[tt].mid+1,yy,(tt<<1)+1);
tree[tt].num=tree[tt<<1].num+tree[(tt<<1)+1].num;
return;
}
void add(int x,int y,int numb,int g)
{
tree[g].num+=(y-x+1)*numb;
if((tree[g].l==x)&&(tree[g].r==y))return;
if(x<=tree[g].mid)add(x,min(tree[g].mid,y),numb,g<<1);
if(y>tree[g].mid)add(max(x,tree[g].mid+1),y,numb,(g<<1)+1);
return;
}
void search(int xx,int yy,int g)
{
if(tree[g].l==xx&&tree[g].r==yy)
{
ans+=tree[g].num;
return;
}
if(xx<=tree[g].mid)search(xx,min(tree[g].mid,yy),g<<1);
if(yy>tree[g].mid)search(max(xx,tree[g].mid+1),yy,(g<<1)+1);
}
int main()
{
scanf("%d%d",&n,&t);
build(1,n,1);
for(int i=1;i<=n;i++)
{
scanf(" %c",&ml);
if(ml=='I')
{
scanf("%d%d%d",&x,&y,&num);
if(x>y)swap(x,y);
add(x,y,num,1);
}
if(ml=='O')
{
scanf("%d%d%d",&x,&y,&num);
if(x>y)swap(x,y);
add(x,y,-num,1);
}
if(ml=='S')
{
scanf("%d%d",&x,&y);
if(x>y)swap(x,y);
ans=0;
search(x,y,1);
printf("%lld\n",ans);
}
}
return 0;
}
题目F 寻找(Find)
题目大意
给出一系列的链接,查询判断两个元素是否在同一条链上。
大致思路
本题难度低于E题。可以建立一个数组,在一条链上随机选定一个点作为这条链上所有节点的根节点。查询时只需要查询一个节点的最终根节点是否与另一个最终根节点一致即可判定是否在一条链上,如果最终根节点不在当前节点记录的父节点时,更新当前节点的父节点为最终根节点以减少之后进行查询时消耗的时间。
如果对本题有相关学习的兴趣,可以搜索"并查集"并进行相关学习。
参考代码
#include <cstdio>
#include <cstring>
using namespace std;
#define MAXN 100001
int father[MAXN],n,m,ans;
int x,y;
bool fff[MAXN];
int getfather(int v)
{
if(fff[v])return 0;
fff[v]=1;
if (father[v]==v) return v;
ans=getfather(father[v]);
if(ans==0)return father[v]=v;else return father[v]=ans;
}
bool same(int x,int y){
memset(fff,0,sizeof(fff));
int xx=getfather(x);
memset(fff,0,sizeof(fff));
int yy=getfather(y);
return (xx==yy);
}
void judge(int x,int y)
{
int fx,fy;
memset(fff,0,sizeof(fff));
fx=getfather(x);
memset(fff,0,sizeof(fff));
fy=getfather(y);
if(fx==fy) return;
father[fx]=fy;
}
int main()
{
scanf("%d",&n);
for(int i=1; i<=n; i++) father[i]=i;
for(int i=1; i<=n; i++)
{
scanf("%d",&x);
if(x==0)continue;
judge(x,i);
}
scanf("%d",&m);
for (int i=1;i<=m;i++)
{
scanf("%d%d",&x,&y);
if(same(x,y))printf("Yes\n");else printf("No\n");
}
return 0;
}