第13届景驰-埃森哲杯广东工业大学ACM程序设计大赛题目题解

本文精选了多项算法竞赛题目,并提供了详细的解题思路与代码实现,涵盖了数组操作、字符串处理、数学技巧及图形分析等多个方面。

点击查看比赛题目

A-跳台阶

#include <iostream>
#include <stdio.h>

using namespace std;

long long f[50];
void fun() {
    f[1] = 1;
    long long sum = f[1];
    for(int i = 2; i <= 30; i++) {
        f[i] = sum+1;
        sum += f[i];
    }
}
int main() {
    int n,t;
    fun();
    scanf("%d",&t);
    while(t--) {
        scanf("%d",&n);
        printf("%lld\n",f[n]);
    }
    return 0;
}

B-跳一跳,很简单的(待补)


C-平分游戏

/***********************************************************
Date 2018/3/28 15:27
Author Ms. Wen

解题思路:
参考他人思路:https://blog.csdn.net/hnust_Derker/article/details/79684711
*************************************************************/
#include <iostream>
#include <stdio.h>
#include <cmath>
#include <algorithm>
#include <string.h>

using namespace std;

typedef long long ll;
const int maxn = 1000000+10;
ll a[maxn],b[maxn],c[maxn];
int vis[maxn];
ll cal(ll n,ll aver) {
    c[0] = 0;
    for(int i = 1; i < n; i++) {
       c[i] = c[i-1] + b[i] - aver;
    }
    sort(c,c+n);
    ll temp = c[n/2];
    ll ans = 0;
    for(int i = 0; i < n; i++) {
        ans += abs(temp-c[i]);
    }
    return ans;
}
int main() {
    ll n,k,sum;
    while(~scanf("%lld%lld",&n,&k)) {
        //隔k个人,则编号要跳k+1个
        k++;
        sum = 0;
        for(int i = 0; i < n; i++) {
            scanf("%lld",&a[i]);
            sum += a[i];
        }
        //无法对n各人平分
        if(sum%n) {
            puts("gg");
        }
        //不是所有人都能参加游戏
        else if(k >= n) {
            //此时必须保证所有人已经是平均值
            bool flag = true;
            for(int i = 0; i < n; i++) {
                if(a[i] != sum/n) {
                    puts("gg");
                    flag = false;
                    break;
                }
            }
            if(flag) {
                printf("0\n");
            }
        }
        else {
            ll cnt,temp,ans=0;
            bool flag = true;
            memset(vis,0,sizeof(vis));
            for(int i = 0; i < n; i++) {
                if(vis[i]==0) {
                    temp = 0;
                    cnt = 1;
                    for(int j = i; vis[j]==0 ; j = (j+k)%n) {
                        b[cnt++] = a[j];
                        temp += a[j];
                        vis[j] = 1;
                    }
                    //无法整除
                    cnt--;
                    if(temp%cnt) {
                        puts("gg");
                        flag = false;
                        break;
                    }  //可以整除,但是无法和总体平均值相同。
                    if(temp/cnt != sum/n) {
                        puts("gg");
                        flag = false;
                        break;
                    }
                    ans += cal(cnt,sum/n);
                }
            }
            if(flag) {
                printf("%lld\n",ans);
            }
        }
    }
    return 0;
}

D-psd面试

/**********************************************************
Date 2018-03-25 09:52:10
Author Ms. Wen

解题思路:
比赛完了,看了别人的代码终于明白题目意思了,题目给出的
串是可以删除任意字符的,因此求区间i-j中最多有多少成对的
字符,所谓成对即相等。
*********************************************************/

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>

using namespace std;

const int maxn = 1500;
char str[maxn];
int dp[maxn][maxn];   //dp[i][j]i到j区间的字符有多少个成对的。
int main() {
    while(~scanf("%s",str)) {
        int len = strlen(str);
        for(int i = 0; i < len; i++) {
            if(str[i]>='A' && str[i]<='Z') {
                str[i] += 32;
            }
        }
        memset(dp,0,sizeof(dp));
        for(int i = len-1; i >= 0; i--) {
            dp[i][i] = 1;
            for(int j = i+1; j < len; j++) {
                if(str[i] == str[j]) dp[i][j] = max(dp[i][j],dp[i+1][j-1]+2);
                else dp[i][j] = max(dp[i+1][j],dp[i][j-1]);
            }
        }
        printf("%d\n",len-dp[0][len-1]);

    }
    return 0;
}



E-回旋星空

/***********************************************************
Date 2018/3/27 15:08
Author Ms. Wen

解题思路:枚举每个点,对于点i,使用map统计距点i距离为dist的点有
多少个,然后可以从里面任意选两个点,比如n个点,任意选2个点,共
n*(n-1)/2,又因为两个点可以有先后顺序,则方案数是n*(n-1)。
*************************************************************/
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <map>

using namespace std;

const int maxn = 1e3+10;
int x[maxn],y[maxn];
int calDist(int i,int j) {
    return (x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]);
}
int main() {
    int t,n,dist;
    scanf("%d",&t);
    while(t--) {
        scanf("%d",&n);
        for(int i = 1; i <= n; i++) {
            scanf("%d%d",&x[i],&y[i]);
        }
        int ans = 0,num;
        for(int i = 1; i <= n; i++) {
            map<int,int>m;
            map<int,int>::iterator it;
            for(int j = 1; j <= n; j++) {
                dist = calDist(i,j);
                m[dist]++;
            }
            for(it = m.begin(); it != m.end(); it++) {
                if(it->second >= 2) {
                    num = it->second;
                    ans = ans + num*(num-1);
                }
            }
        }
        if(ans == 0) {
            printf("WA\n");
        }
        else {
            printf("%d\n",ans);
        }
    }
    return 0;
}

F-等式

/***********************************************************
Date 2018/3/27
Author Ms. Wen

解题思路:考察数论中的唯一分解定理。但是关键是对式子做变换。
设x=n+a,y=n+b。 1/x+1/y=n 可以化成n*n = a*b。化成这样后,只要
知道n*n的因子个数,就可以得到答案。
求解因子,根据基础数论知识可知任何正整数n都可以表示成如下形式:
n = p1^e1*p2^e2...pn^en (其中p1,p2,...,pn都是素数)。
其因子个数就是:(e1+1)*(e2+1)*...*(en+1),则

n*n = p1^(2*e1)*p2^(2*e2)*...p3^(2*e3)。
其因子个数就是:(2*e1+1)*(2*e2+1)*...*(2*en+1)。
因为x<=y,则需要a<=b,最后因子数除2加1就是答案。
主要就是变换难想,比赛的时候也没想出来。
*************************************************************/
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <math.h>

using namespace std;

int main() {
    int t,n,cnt;
    scanf("%d",&t);
    while(t--) {
        scanf("%d",&n);
        long long ans = 1;
        for(int i = 2; i<=sqrt(n); i++) {
            if(n%i == 0) {
                cnt = 0;
                while(n%i==0) {
                    cnt++;
                    n = n/i;
                }
                ans = ans*(2*cnt+1);
            }
        }
        //除过后n是素数的情况
        if(n != 1) {
            ans = ans*3;
        }
        printf("%lld\n",(ans+1)/2);
    }
    return 0;
}

G-旋转矩阵

/**********************************************************
Date 2018-03-25 10:43:38
Author Ms. Wen

解题思路:
设左转为加1,右转是减1,左转右转可以抵消,先算出最终是像某个方向
转了num次。然后对4取余。
1.num%4是0,原样输出。
2.num%4是2,按行从大到小,列从大到小打出。
3.num%4==3&&是左转,num%4==1&&是右转,左转三次和右转一次相同。
4.左转一次和右转三次的情况。
只要捋清楚打印时的方向,就能弄对,这个为了更清楚,可以把图画到
纸上,这样拿纸去旋转要比脑子单纯想要可靠的多。
*********************************************************/
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <cmath>

using namespace std;

char Map[50][50];
char str[1005];
int n,m;
int main() {
    int t;
    scanf("%d",&t);
    while(t--) {
        scanf("%d%d",&n,&m);
        for(int i = 1; i <= n; i++) {
            for(int j = 1; j <= m; j++) {
                scanf(" %c",&Map[i][j]);
            }
        }
        scanf("%s",str);
        int cnt = 0;
        for(int i = 0; i < (int)strlen(str); i++) {
            if(str[i]=='L') {
                cnt++;
            }
            else {
                cnt--;
            }
        }
        if(abs(cnt)%4 == 0) {
            printf("%d %d\n",n,m);
            for(int i = 1; i <= n; i++) {
                for(int j = 1; j <= m; j++) {
                    printf("%c",Map[i][j]);
                }
                printf("\n");
            }
        }
        else if(abs(cnt)%4 == 2) {
            printf("%d %d\n",n,m);
            for(int i = n; i > 0; i--) {
                for(int j = m; j > 0; j--) {
                    printf("%c",Map[i][j]);
                }
                printf("\n");
            }
        }
        else if((cnt>0&&cnt%4==3) || (cnt<0&&(abs(cnt)%4==1))){
            //左转三次或右转一次。
            printf("%d %d\n",m,n);
            for(int i = 1; i <= m; i++) {
                for(int j = n; j >= 1; j--) {
                    if(Map[j][i]=='-') printf("|");
                    else if(Map[j][i]=='|') printf("-");
                    else printf("%c",Map[j][i]);
                }
                printf("\n");
            }
        }
        else {
            printf("%d %d\n",m,n);
            for(int i = m; i >= 1; i--) {
                for(int j = 1; j <= n; j++) {
                    if(Map[j][i]=='-') printf("|");
                    else if(Map[j][i]=='|') printf("-");
                    else printf("%c",Map[j][i]);
                }
                printf("\n");
            }
        }
        printf("\n");
    }
    return 0;
}

H-哲哲的疑惑(待补)


I-填空题

输出ac即可

J-强迫症的序列

/**********************************************************
Date 2018-03-24 16:32:02
Author Ms. Wen

解题思路:对数组从小到大排序,先让第二小不动,让最小往其上面并。
让后让第三小不变,让前两个往它身上并,一直这样考虑。
*********************************************************/
#include <iostream>
#include <stdio.h>
#include <algorithm>

using namespace std;

const int maxn = 1e6+10;
long long a[maxn];
int main() {
    int t,n;
    scanf("%d",&t);
    while(t--) {
        scanf("%d",&n);
        for(int i = 0; i < n; i++) {
            scanf("%lld",&a[i]);
        }
        sort(a,a+n);
        int cnt = 0;
        a[cnt++] = a[0];
        for(int i = 1; i < n; i++) {
            if(a[i]!=a[i-1]) {
                a[cnt++] = a[i];
            }
        }
        long long sum = 0,tmp;
        for(int i = 1; i < cnt; i++) {
            tmp = a[i]+sum-a[i-1];   //前i-1个并到第i个需要多少次操作
            a[i] = a[i] + sum;       //更新高度
            sum += tmp;
        }
        printf("%lld %lld\n",sum,a[cnt-1]);
    }
    return 0;
}

K-密码

/**********************************************************
Date 2018-03-24 
Author Ms. Wen

解题思路:总结字符之间间隔的规律,注意一点,n = 1时,单独处理,
其他情况按照间隔规律处理。
*********************************************************/
#include <iostream>
#include <stdio.h>
#include <string.h>
 
using namespace std;
 
const int maxn = 1e7;
char str[maxn];
char ans[maxn];
int main() {
    int t,n;
    scanf("%d",&t);
    while(t--) {
        scanf("%d ",&n);
        gets(str);
        if(n > 1) {
            int cnt = 0;
            int len = strlen(str);
            int m = 2*n-2;
            for(int i = 1; i <= len; i+=m) {
                ans[cnt++] = str[i-1];
            }
            for(int i = 2; i <= n-1; i++) {
                int tmp = 2*n-2*i;
                for(int j = i; j <= len; j+=m) {
                    ans[cnt++] = str[j-1];
                    if(j+tmp <= len) {
                        ans[cnt++] = str[j+tmp-1];
                    }
                }
            }
            for(int i = n; i <= len; i+=m) {
                ans[cnt++] = str[i-1];
            }
            ans[cnt] = '\0';
            puts(ans);
        }
        else {
            puts(str);
        }
    }
    return 0;
}

L-用来作弊的药水

/**********************************************************
Date 2018-03-24 14:16:28
Author Ms. Wen

解题思路:判断x^a = y^b 两边对y取对数,logy(x^a) = b
a * logy(x) = b ,但是C语言中没有以任意数为底的对数的计算,
C语言math函数中的log是默认以10为底的,所以需要判断。
a * log(x)/log(y) = b  ->  a*log(x) = b*log(y)
最终判断这个式子就可以了。
*********************************************************/
#include <stdio.h>
#include <math.h>
#define eps 1e-4
int main() {
    int x,a,y,b,t;
    scanf("%d",&t);
    while(t--) {
        scanf("%d%d%d%d",&x,&a,&y,&b);
        double tmp1 = a*log(x);
        double tmp2 = b*log(y);
        if(fabs(tmp1-tmp2)<eps) {
            printf("Yes\n");
        }
        else {
            printf("No\n");
        }
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值