电子科技大学第八届ACM趣味程序设计竞赛第二场题解

A阴阳师?这游戏没有ssr!
题解:


ans = sigma(1 - (1 - Pi/100)^ 2);
标程:
#include <stdio.h>
#include <stdlib.h>


int main()
{
    int N,p;
    double ans=0.0;
    scanf("%d",&N);
    ans=N;
    while(N--)
    {
        scanf("%d",&p);
        p=100-p;
        ans=ans-p*p*1.0/10000.0;
    }
    printf("%.3lf",ans);
    return 0;
}




B可怜的非洲银
题解:模拟每个碎片按照顺序得到即可,注意每天先购买1个F碎片,输出有先后顺序
#include <stdio.h>
#include <stdlib.h>


int main()
{
    int cnt[26];
    char s[150];
    int N,K,i,j,day=0,flag=0;
    for(i=0;i<26;i++)cnt[i]=0;
    scanf("%d%d",&N,&K);
    for(i=0;i<N;i++)
    {
        scanf("%s",&s);
        cnt['F'-'A']++;
        for(j=0;s[j]!='\0';j++)
        {
            if(flag==1)
                break;
            cnt[s[j]-'A']++;
            if(cnt['F'-'A']>=50){
                printf("%d F",i+1);
                flag=1;break;
            }
            if(cnt[s[j]-'A']>=50){
                printf("%d %c",i+1,s[j]);
                flag=1;break;
            }


        }
    }
    if(flag==0&&K>N)printf("AMNZ");
    else if(flag==0)printf("Feizhou Yin");
    return 0;
}
C简单的数学题
题解:
a * 10 + b   -  (b * 10 + a )是9的倍数 说明交换相邻数位之后 原数和新数的差是9的倍数,那你可以通过不断交换获得最小的数,即将各个位数从小到大排列后输出即可
#include <stdio.h>
#include <stdlib.h>


int main()
{
    int cnt[10]={0};
    char ans[1010];
    char s[1010];
    scanf("%s",&s);
    int i,p=0;
    for(i=0;s[i]!='\0';i++)
        cnt[s[i]-'0']++;
    for(i=0;i<10;i++)
    {
        while(cnt[i])
        {
            cnt[i]--;
            ans[p++]=i+'0';
        }
    }
    ans[p++]='\0';
    printf("%s",ans);
    return 0;
}
D 我想上厕所
题解:
三种情况0个人上错厕所 输出02个人上错厕所 选择两个方向最短的3个人上错厕所 推导公式:两个人之间有个间隔,总共有3个间隔,选取其中最短的2个
标程
#include<stdio.h>
int min(int a,int b)
{
    return a<b?a:b;
}
int main()
{
    int n,i,cnt,ans;
    int a,p[5];
    scanf("%d",&n);cnt=0;
    for(i=1;i<=n;i++)
    {
        scanf("%d",&a);
        if (a!=i)
        {
            cnt++;
            p[cnt]=i;
        }
    }
    if (cnt==0) ans=0;
    if (cnt==2)
    {
        ans=min(p[2]-p[1],n-p[2]+p[1]);
        ans=2*ans-1;
    }
    if (cnt==3)
    {
        ans=min(p[3]-p[1],n-(p[2]-p[1]));
        ans=min(ans,n-(p[3]-p[2]));
        ans=2*ans-2;
    }
    printf("%d\n",ans);
    return 0;
}
E-Megumin的爆裂魔法
该题为计算几何经典问题——最小圆覆盖的简化版,即n<=3的情况。
显然,n==1时,最小圆退化为一个点。
n==2时,最小圆半径为连接两点的线段长度的一半,圆心位于线段中点。
n==3时,若三点构成一个钝角三角形,则最小圆半径为三角形最长边长度的一半,圆心位于最长边中点。
若构成的三角形是锐角或直角三角形,则最小圆为其外接圆。
考虑如何求外接圆圆心:
<法一>
任取三角形的两条边,作它们的垂直平分线,求交点即可。
但是在程序中如何实现?如果仅仅依靠高中的斜截式直线方程,我们需要讨论我们取的这两条边所在直线的斜率是否存在,是否为零,是否存在且非零这几种情况,在第一份标程中,命题人所给出的就是这样的做法。
该题的情况比较多,需要讨论全面,同时对实数的处理也是一项挑战。
在算法竞赛中,我们处理直线,一般用其上的一个点和一个方向向量,即参数式表示,这样可以避免分类讨论,比较容易求得垂直平分线和交点,请读者自行思考。

#include<stdio.h>
#include<math.h>
#define EPS 0.00000001
int n;
typedef struct {double x,y;}Point;
typedef Point Vector;
double sqr(double x){return x*x;}
double dis(Point a,Point b){return sqrt(sqr(a.x-b.x)+sqr(a.y-b.y));}
Vector Minus (Point a,Point b){return (Vector){a.x-b.x,a.y-b.y};}
double dot(Vector a,Vector b){return a.x*b.x+a.y*b.y;};
int check(Point a,Point b){
 if(fabs(a.x-b.x)<EPS) return -1;
 if(fabs(a.y-b.y)<EPS) return 1;
 return 0;
}
Point a[3];
int main()
{
// freopen("09.in","r",stdin);
// freopen("09.out","w",stdout);
 int i;
 scanf("%d",&n);
 for(i=0;i<n;++i) scanf("%lf%lf",&a[i].x,&a[i].y);
 if(n==1) {printf("%.4f %.4f %.4f\n",0.0,a[0].x,a[0].y); return 0;}
 if(n==2) {printf("%.4f %.4f %.4f\n",dis(a[0],a[1])*0.5,(a[0].x+a[1].x)*0.5,(a[0].y+a[1].y)*0.5); return 0;}
 if(dot(Minus(a[1],a[0]),Minus(a[2],a[0]))<EPS) {printf("%.4f %.4f %.4f\n",dis(a[1],a[2])*0.5,(a[1].x+a[2].x)*0.5,(a[1].y+a[2].y)*0.5); return 0;}
 else if(dot(Minus(a[1],a[2]),Minus(a[0],a[2]))<EPS) {printf("%.4f %.4f %.4f\n",dis(a[1],a[0])*0.5,(a[1].x+a[0].x)*0.5,(a[1].y+a[0].y)*0.5); return 0;}
 else if(dot(Minus(a[0],a[1]),Minus(a[2],a[1]))<EPS) {printf("%.4f %.4f %.4f\n",dis(a[0],a[2])*0.5,(a[0].x+a[2].x)*0.5,(a[0].y+a[2].y)*0.5); return 0;}
 int flag1=check(a[0],a[1]),flag2=check(a[0],a[2]);
 if(flag1!=0 && flag2!=0){
    Point tmp=a[0]; a[0]=a[1]; a[1]=tmp;
    flag2=check(a[0],a[2]);
 }
 if(flag2!=0){
    Point tmp=a[1]; a[1]=a[2]; a[2]=tmp;
    int tm2=flag1; flag1=flag2; flag2=tm2;
 }//line(a[0],a[1])为特殊或一般;line(a[0],a[2])为一般
 double k2=(a[2].y-a[0].y)/(a[2].x-a[0].x);
 double b2=a[0].y-a[0].x*k2;
 double k4=(-1.0)/k2;
 Point mp2=(Point){(a[0].x+a[2].x)*0.5,(a[0].y+a[2].y)*0.5};
 double b4=mp2.y-mp2.x*k4;
 if(flag1==-1){
  Point ap; ap.y=(a[0].y+a[1].y)*0.5;
  ap.x=(ap.y-b4)/k4;
  printf("%.4f %.4f %.4f\n",dis(ap,a[0]),ap.x,ap.y);
 }
 else if(flag1==1){
  Point ap; ap.x=(a[0].x+a[1].x)*0.5;
  ap.y=k4*ap.x+b4;
  printf("%.4f %.4f %.4f\n",dis(ap,a[0]),ap.x,ap.y);
 }
 else/*flag1==0*/{
  double k1=(a[1].y-a[0].y)/(a[1].x-a[0].x);
  double b1=a[0].y-a[0].x*k1;
  double k3=(-1.0)/k1;
  Point mp1=(Point){(a[0].x+a[1].x)*0.5,(a[0].y+a[1].y)*0.5};
  double b3=mp1.y-mp1.x*k3;
  Point ap; ap.x=(b4-b3)/(k3-k4); ap.y=k3*ap.x+b3;
  printf("%.4f %.4f %.4f\n",dis(ap,a[0]),ap.x,ap.y);
 }
 return 0;
}
<法二>
设三角形三个顶点分别为(x1,y1),(x2,y2),(x3,y3),待求的圆心为(x,y)。
则显然有以下方程组成立
(x-x1)^2+(y-y1)^2 = (x-x2)^2+(y-y2)^2 = (x-x3)^2+(y-y3)^2
化简为
{2*(x2-x1)*x+2*(y2-y1)*y = -x1^2-y1^2+x2^2+y2^2
{
{2*(x3-x1)*x+2*(y3-y1)*y = -x1^2-y1^2+x3^2+y3^2
该二元一次方程组可以用克拉默法则方便求解。
这种做法不需要讨论三条边的种种位置关系,代码也比较短,在第二份标程中给出。


#include<stdio.h>
#include<math.h>
#define EPS 0.00000001
int n;
typedef struct {double x,y;}Point;
typedef Point Vector;
double sqr(double x){return x*x;}
double dis(Point a,Point b){return sqrt(sqr(a.x-b.x)+sqr(a.y-b.y));}
Vector Minus (Point a,Point b){return (Vector){a.x-b.x,a.y-b.y};}
double dot(Vector a,Vector b){return a.x*b.x+a.y*b.y;};
Point a[3];
int main()
{
// freopen("01.in","r",stdin);
// freopen("09.out","w",stdout);
int i;
scanf("%d",&n);
for(i=0;i<n;++i) scanf("%lf%lf",&a[i].x,&a[i].y);
if(n==1) {printf("%.4f %.4f %.4f\n",0.0,a[0].x,a[0].y); return 0;}
if(n==2) {printf("%.4f %.4f %.4f\n",dis(a[0],a[1])*0.5,(a[0].x+a[1].x)*0.5,(a[0].y+a[1].y)*0.5); return 0;}
if(dot(Minus(a[1],a[0]),Minus(a[2],a[0]))<EPS) {printf("%.4f %.4f %.4f\n",dis(a[1],a[2])*0.5,(a[1].x+a[2].x)*0.5,(a[1].y+a[2].y)*0.5); return 0;}
else if(dot(Minus(a[1],a[2]),Minus(a[0],a[2]))<EPS) {printf("%.4f %.4f %.4f\n",dis(a[1],a[0])*0.5,(a[1].x+a[0].x)*0.5,(a[1].y+a[0].y)*0.5); return 0;}
else if(dot(Minus(a[0],a[1]),Minus(a[2],a[1]))<EPS) {printf("%.4f %.4f %.4f\n",dis(a[0],a[2])*0.5,(a[0].x+a[2].x)*0.5,(a[0].y+a[2].y)*0.5); return 0;}
double b[3],A[3][3];
A[1][1]=2.0*(a[1].x-a[0].x);
A[1][2]=2.0*(a[1].y-a[0].y);
b[1]=-a[0].x*a[0].x-a[0].y*a[0].y+a[1].x*a[1].x+a[1].y*a[1].y;
A[2][1]=2.0*(a[2].x-a[0].x);
A[2][2]=2.0*(a[2].y-a[0].y);
b[2]=-a[0].x*a[0].x-a[0].y*a[0].y+a[2].x*a[2].x+a[2].y*a[2].y;
double tmp=A[1][1]*A[2][2]-A[1][2]*A[2][1];
double tmp1=b[1]*A[2][2]-A[1][2]*b[2];
double tmp2=A[1][1]*b[2]-b[1]*A[2][1];
Point ans;
ans.x=tmp1/tmp;
ans.y=tmp2/tmp;
printf("%.4f %.4f %.4f\n",dis(ans,a[0]),ans.x,ans.y);
return 0;
}



感谢大家参加本场趣味赛,趣味赛评奖结果我们会在群内公布,接下来还有两场更为精彩的比赛,欢迎大家参加!

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ACM程序设计竞赛基础教程(第2版)》是一本经典的程序设计竞赛教材,旨在帮助学习者掌握ACM程序设计竞赛的基础知识和解题技巧。 该教材以清晰简明的语言详细介绍了ACM竞赛所需的基本知识,包括数据结构、算法设计与分析、动态规划、图论等。同时,它提供了大量的例题和习题,帮助学习者巩固知识,培养解题能力。 与其他类似的教材相比,这本教材具有以下几个特点: 首先,该教材内容全面,不仅介绍了ACM竞赛中常见的基础知识,还涵盖了一些高级内容。通过学习这本教材,学习者能够建立起扎实的程序设计基础,为进一步深入学习和应用打下坚实的基础。 其次,该教材注重实战,提供了大量的例题和习题。这些题目都是经过精心挑选和设计的,能够帮助学习者理解和掌握各种解题技巧。通过反复练习,学习者能够逐渐提高自己的编程水平和解题能力。 最后,该教材配有详细的讲解和解答,帮助学习者更好地理解和掌握知识点。无论是初学者还是有一定基础的学习者,都可以根据自己的情况选择性地学习和提升。 《ACM程序设计竞赛基础教程(第2版)》是一本值得推荐的优秀教材,它不仅适用于参加ACM竞赛的学生,也适用于对算法和程序设计感兴趣的人。通过学习这本教材,学习者能够提高自己的编程能力,锻炼解决问题的思维方式,为将来的学习和工作打下良好的基础。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值