German Collegiate Programming Contest 2018(GCPC)

目录

Gym - 102021B: Battle Royale

Gym - 102021D:Down the Pyramid

 Gym - 102021E :Expired License

 Gym - 102021F :Fighting Monsters

 Gym - 102021I: It’s Time for a Montage

Gym - 102021L: Logic Puzzle 


题目链接:http://codeforces.com/gym/102021

 

Gym - 102021B: Battle Royale

题意:平面内有一个大圆,大圆里面有两个点(我假设A和B)和一个小圆,这个小圆在A,B之间。问从A到B的最短距离。(小圆和AB两点永远在大圆里面,并且AB两点不在小圆里面,并且走小圆的边缘也是允许的)

思路:示意图如下图所示,分析可知最短距离就是L1+L2+L3的值,其中L1,L2是三角形的弦,L3是圆的一段弧长。L1,L2好求,通过勾股定理即可,求L3时要用到弧长公式“ L=R*θ”(θ为弧度制)。所以求角度a2时,要求出其中的大角和两边的小角,相减即可得到a2。求出da求三个角度a1,a2,a3时,要用到acos(number)函数求角度(number为余弦值),最后即可求出L3的弧长。(自己做的是前面分析出图都很简单,就是求角度时不知道可以用这个函数,卡了半天)

German Collegiate Programming Contest 2018(GCPC)

#include<iostream>
#include<cmath>
#include<cstdio>
using namespace std;

int main()
{
    int  x1,y1,x2,y2,x0,y0,r0,x,y,r;
    scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
    scanf("%d%d%d",&x,&y,&r);
    scanf("%d%d%d",&x0,&y0,&r0);
    double len1=sqrt((x1-x0)*(x1-x0)+(y1-y0)*(y1-y0));
    double len2=sqrt((x2-x0)*(x2-x0)+(y2-y0)*(y2-y0));
    double len=sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
    double ans1=sqrt(len1*len1-r0*r0);//弦L1
    double ans2=sqrt(len2*len2-r0*r0);//弦L2
    //ang为圆心角
    double ang=acos((len1*len1+len2*len2-len*len)/(2*len1*len2));///加括号!!!
    ang=ang-acos(r0/len1)-acos(r0/len2);
    double ans=ans1+ans2+r0*ang;
    printf("%.10lf\n",ans);
    return 0;
}

 小总结:1、就是求角函数 acos(number)其中number为余弦值,acos()所求的值为弧度制。2、用到除号“/”当分母有多项时一定记得加括号!!这些小细节真的很重要,有时候这些细节忘了一旦程序报错了很难找出自己哪错了。所以要重视这些细节。

 

Gym - 102021D:Down the Pyramid

题意:给你一层数,让你求出它下面的一层数,上面的每一个数都是下层相邻两个数的和。(就像图中的数字金字塔一样)。问你下面一层有多少种满足的方案。

思路:假设给出的值为,a1,a2,a3,a3...,假设下面的第一个数b0=x,那么可以推得第二个数b1=a1-x,第三个数就是a2-(a1-x),以此类推。其中bo...bn要满足的条件就是每一项都大于等于零。所以就可推得关于x 的不等式,所以就可以求出x的一个最小的范围,所以就可求出方案数。

其实这个思路一开始也没懂,但是在草稿纸上写了一下就突然明白了,

b1=a1-x>=0

b2=a2-(a1-x)>=0,

b3=a3-(a2-(a1-x))>=0

...

所以可知是一个递推公式:x=a[i]-x;每一项的x都要满足>=0

当下标为奇数时,取上界最小的一个,当下标为偶数时,取下界最大的一个(还不懂就在草稿纸上写出几个不等式就知道怎么回事了)

(这题比赛的时候没做出来,当时感觉这道题跟之前做过的一道熄灯问题很像,都是有一种连带,枚举局部就能得到整体的解。所以比赛的时候我一直在想可不可以枚举第一个数,然后判断最后一个数是否满足条件。显然看数据就知道我错了,这道题也是想了半天,后来看了其他队的做法,和网上的做法,不得不服,脑子真是个好东西可惜我没有。)

#include<iostream>
#include<algorithm>
#include<cstdio>
#define maxn 1000050
using namespace std;
int a[maxn];
int n;
int main()
{
    ios::sync_with_stdio(false);
//    freopen("in.txt","r",stdin);
    while(cin>>n)
    {
        for(int i=1;i<=n;i++)
            cin>>a[i];
        int low=0, high=a[1];
        int x=0;
        for(int i=1;i<=n;i++)
        {
            x=a[i]-x;
            if(i%2)//奇数
            {
                high=min(high,x);//取上界的最小
            }
            else
            {
                low=max(low,-x);//取下界额最大
            }
        }
        if(low>high)
            cout<<0<<endl;
        else
            cout<<high-low+1<<endl;//区间中满足条件的方案数
    }
    return 0;
}

 Gym - 102021E :Expired License

题意:给你一对浮点数,让你判断这对浮点数的比值能否用一对素数的比值所表示,如果可以就输出两个素数。

分析:首先将所给数乘以100000并四舍五入,然后两数在除以他们的最大公因子判断是否为素数。其中里面包含一种特殊情况,就是当两数相等时,直接输出2,2

(一开始的判断素数的思路是每次都判断一次,但是这样一直过不了,直到预处理存下所有素数再判断才过了)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#define maxn 10000050
using namespace std;
bool isPrime[maxn];
int n;
int gcd(int a,int b)
{
    return a%b?gcd(b,a%b):b;
}
//bool checksushu(int x)
//{
//    for(int i=2;i<x;i++)
//        if(x%i==0)
//            return false;
//    return true;
//}
void init()
{
    for(int i=2;i<=maxn;i++)
        isPrime[i]=true;
    for(int i=2;i<=maxn;i++)
    {
        if(isPrime[i])
        {
            for(int j=2*i;j<=maxn;j+=i)
                isPrime[j]=false;
        }
    }
}

int main()
{
    ios::sync_with_stdio(false);
//    freopen("in.txt","r",stdin);
    init();
    int t;
    double a,b;
    cin>>t;
    while(t--)
    {
        cin>>a>>b;
        a=round(a*100000);
        b=round(b*100000);
        int num=gcd(a,b);
        a/=num;
        b/=num;
        int x=a,y=b;
        if(x==y)
            cout<<2<<" "<<2<<endl;
        else
        {
            if(isPrime[x]&&isPrime[y])
                cout<<x<<" "<<y<<endl;
            else
                cout<<"impossible"<<endl;
        }
    }
    return 0;
}

 Gym - 102021F :Fighting Monsters

题意:给定N个怪兽,问能否选出两个怪兽,使得他们相互攻击,最后活着的怪兽剩1滴血,死的怪兽的血为零或者为负数。

分析:通过分析可知,满足条件的最后两怪兽的血量一定是 1和0,(假设如果最后是-1,1的话,那么这个-1怪兽肯定是被能力值为1的怪兽所攻击的,那么它们上一个状态的血量就一定是0,1,所以不会出现-1,1 的情况,所以1,-2、1,-3,的更不可能出现了)。所以通过最后的血量1,0网上推可能的数字对时,可以发现规律就是斐波那契数列相邻的两个数。所以问题就变成求相邻的两个斐波那契数就可以了。

(这题比赛的时候其实已经想出来思路了,但是敲代码的时候一直很着急,越着急越不过导致我心态炸了,这以后千万要注意,比赛保持一个好的心态非常重要!)

#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdio>
const int maxn = 1e8+50;
const int maxx=100050;
using namespace std;
struct node
{
    int x,num;//x表示下标,num表示能力值
}m[maxx];
struct rule
{
    bool operator()(const node &a1,const node &a2)
    {
        return a1.num<a2.num;
    }
};
int n;
int b[1000];
int k;
int s[maxx];
void feibo()//首先预处理先存好斐波那契数
{
    b[0]=1;
    b[1]=1;
    k=2;
    while(1)
    {
        b[k] = b[k-1]+b[k-2];
        if(b[k]>=maxn)
            break;
        k++;
    }
}
int main()
{
    feibo();
    while(cin>>n)
    {
        for(int i=1;i<=n;i++)
        {
            cin>>m[i].num;
            m[i].x=i;
        }
        sort(m+1,m+n+1,rule());//按照能力值排序
        for(int i=1;i<=n;i++)
            s[i]=m[i].num;
        int flag=0;
        int i,j;
        int pp;
        for(i=1;i<n;i++)
        {
            int p=lower_bound(b,b+k,m[i].num)-b;//判断这个数是不是斐波那契数
            if(b[p]!=m[i].num)
                continue;
            else
            {//如果是的话,就查找他相邻的斐波那契数
                pp=lower_bound(s+i+1,s+n+1,b[p+1])-s;
                if(s[pp]==b[p+1])
                {
                    flag=1;
                    break;
                }

            }
            if(flag==1)
                break;
        }
        if(flag)
        cout<<m[i].x<<" "<<m[pp].x<<endl;
        else
            cout<<"impossible"<<endl;

    }
    return 0;
}

 

 Gym - 102021I: It’s Time for a Montage

题意:一队英雄和一队怪物按照优先级排队打架,看优先级最高的英雄能否获胜。规定如果英雄和怪物的能力值相同,那么就看优先级较低的下一对的结果。每训练一天可以使所有英雄的能力值都提高1,问要使英雄获胜最少训练多少天。

分析:这个题其实是一道水题,直接暴力就行。最低训练天数就是第一个敌人和英雄的差值。再看后面的英雄有没有比所对的敌人强的,如果有直接输出,如果没有在此能力值基础上加一输出即可。

(这道题其实很水就是题意读了半天才读懂)

#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstring>
using namespace std;
int a[1050];
int b[1050];
int n;
int main()
{

    ios::sync_with_stdio(false);
//    freopen("in.txt","r",stdin);
    while(cin>>n)
    {
        for(int i=0;i<n;i++)
        {
            cin>>a[i];
        }
        for(int i=0;i<n;i++)
            cin>>b[i];
        int d=b[0]-a[0];
        int flag=0;
        if(d<0)
            cout<<0<<endl;
        else
        {
            int p,i;
            for(p=d;p<=1000;p++)
            {
                for(i=0;i<n;i++)
                {
                    if(a[i]+p<b[i])
                        break;
                    else if(a[i]+p==b[i])
                        continue;
                    else
                    {
                        flag=1;
                        break;
                    }
                }
                if(i==n)
                {
                    flag=1;
                    break;
                }
                if(flag==1)
                    break;

            }
            cout<<p<<endl;
        }
    }
    return 0;
}

 

Gym - 102021L: Logic Puzzle 

题意:扫雷问题,给你周围雷的个数,让你标出雷区中的雷。(这题是在雷区外又加了一圈,雷区只是里面的h行w列)

分析: 这题是引入其他博客的思路

不同的是这题中自己位置上的雷也会计算在内。一行一行地解决,从雷区的第一个位置开始,看左上角的位置的雷的数目,如果不为0,就把这个点标记上雷,并将自己和周围的位置全部减去1。

最后如果全部位置在处理完毕之后都为0,就代表存在合法的解。

#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstring>
using namespace std;
int h,w;
int a[110][110];
int dx[9]={1,-1,0,0,1,1,-1,-1,0};
int dy[9]={0,0,-1,1,-1,1,-1,1,0};
int b[110][110];
int main()
{
    ios::sync_with_stdio(false);
    cin>>h>>w;
    memset(b,0,sizeof(b));
    for(int i=0;i<h+2;i++)
        for(int j=0;j<w+2;j++)
            cin>>a[i][j];
    for(int i=1;i<=h;i++)
    {
        for(int j=1;j<=w;j++)
        {
            if(a[i-1][j-1])
            {
                b[i][j]=1;
                for(int k=0;k<9;k++)
                {
                    int nx=i+dx[k];
                    int ny=j+dy[k];
                    a[nx][ny]--;

                }
            }
        }
    }
    int flag=1;
    for(int i=0;i<h+2;i++)
    {
        for(int j=0;j<w+2;j++)
            if(a[i][j]!=0)
            {
                flag=0;
                cout<<"impossible"<<endl;
                break;
            }
        if(flag==0)
            break;
    }
    if(flag==1)
    {
        for(int i=1;i<=h;i++)
        {
            for(int j=1;j<=w;j++)
            {
                if(b[i][j])
                    cout<<"X";
                else
                    cout<<".";
            }
            cout<<endl;
        }
    }

    return 0;
}

(这篇博客应该是我写的最长的一篇了,攒了好几道题的题解没写就打算一起写一块。还有几道题也可以做还没有做,做出来的话再补上题解)