1. 程式人生 > >電子科技大學第八屆ACM趣味程式設計競賽第二場題解

電子科技大學第八屆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;
}


感謝大家參加本場趣味賽,趣味賽評獎結果我們會在群內公佈,接下來還有兩場更為精彩的比賽,歡迎大家參加!