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

本文解析了第13届景驰-埃森哲杯广东工业大学ACM程序设计大赛的多个题目,包括斐波那契变形、数学推导、最长回文子序列等问题,并提供了详细的代码实现。

比赛链接:第13届景驰-埃森哲杯广东工业大学ACM程序设计大赛

A:斐波那契变形

规律:当前项 = 前面所有项之和 + 1,其实打表以后会发现是2^N,懒得改代码了直接交了。

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 35;
long long a[MAXN];
int main()
{
    a[0] = 0; a[1] = 1;
    for (int i = 2; i < MAXN; i++)
    {
        for (int j = 0; j < i; j++)
        {
            a[i] += a[j];
        }
        a[i] += 1;
    }
    int T, n;
    scanf("%d", &T);
    while (T--)
    {
        scanf("%d", &n);
        printf("%lld\n", a[n]);
    }
    return 0;
}
/*
1
1
*/

 

C:数学推导 + 中位数性质

思路:感觉这道题应该改编自UVA ~ 11300。大家可以先看下这道题:UVA ~ 11300 ~ Spreading the Wealth

交换的金币数其实就是花费的时间。这个题分了互不影响几个圈,1 -> 1+(k+1) ->1+2(k+1) -> 1+3(k+1)...,直到出现循环。

加(k+1)的过程中这个值会超过N,要取余。每个圈按UVA~11300那道题处理,求一个最优值出来,累加就是答案。

注意要开long long。还有k=n和n-1的时候是无法传递金币的,所以这个情况要特判。

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1000005;
int n, k;
long long arr[MAXN], A[MAXN], C[MAXN], sum;
bool vis[MAXN];
long long solve(int n, int M)
{
    C[0] = 0;
    for (int i = 1; i < n; i++) C[i] = C[i - 1] + A[i] - M;
    sort(C, C + n);
    long long x1 = C[n/2], ans = 0;
    for (int i = 0; i < n; i++) ans += abs(x1 - C[i]);
    return ans;
}
int main()
{
    ios::sync_with_stdio(false);
    while (cin >> n >> k)
    {
        memset(vis, 0, sizeof(vis));
        sum = 0;
        for (int i = 0; i < n; i++)
        {
            cin >> arr[i];
            sum += arr[i];
        }
        bool flag = true;
        if (k == n || k == n-1)
        {
            for (int i = 0; i < n; i++)
            {
                if (arr[i] != sum / n) flag = false;
            }
            if (flag) printf("0\n");
            else printf("gg\n");
            continue;
        }
        long long ans = 0, cnt;
        for (int i = 0; i < n; i++)
        {
            if (!vis[i])
            {
                cnt = 1; sum = 0;
                for (int j = i; !vis[j]; j = (j + k + 1) % n)
                {
                    vis[j] = true;
                    A[cnt++] = arr[j];
                    sum += arr[j];
                }
                if (sum % (cnt - 1)) { flag = false; break; }
                ans += solve(cnt - 1, sum / (cnt - 1));
            }
        }
        if (flag) printf("%lld\n", ans);
        else printf("gg\n");
    }
    return 0;
}
/*
5 0
2 3 1 5 4
*/

 

D:最长回文子序列

思路:方法①原串与其反转串做LCS。方法②直接DP。

①原串与其反转串做LCS:

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1300;
int dp[MAXN][MAXN];
int LCS(string a, string b)
{
    int len1 = a.size(), len2 = b.size();
    memset(dp, 0, sizeof(dp));
    for (int i = 1; i <= len1; i++)
    {
        for (int j = 1; j <= len2; j++)
        {
            if (a[i - 1] == b[j - 1]) dp[i][j] = dp[i-1][j-1] + 1;
            else dp[i][j] = max(dp[i-1][j], dp[i][j-1]);
        }
    }
    return dp[len1][len2];
}
int main()
{
    ios::sync_with_stdio(false);
    string str, t;
    while (cin >> str)
    {
        for (int i = 0; i < str.size(); i++) str[i] = tolower(str[i]);
        t = str; reverse(t.begin(), t.end());
        cout << t.size() - LCS(str, t) << endl;
    }
    return 0;
}
/*
google
aBc,bAd
*/

 

②DP:(最长回文子序列好像叫LPS)

 

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1300;
int dp[MAXN][MAXN];
int LPS(string str)//最长回文子序列
{
    int len = str.size();
    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] = dp[i+1][j-1]+2;
            else
                dp[i][j] = max(dp[i+1][j], dp[i][j-1]);
        }
    }
    return dp[0][len - 1];
}
int main()
{
    ios::sync_with_stdio(false);
    string str;
    while (cin >> str)
    {
        for (int i = 0; i < str.size(); i++) str[i] = tolower(str[i]);
        cout << str.size() - LPS(str) << endl;
    }
    return 0;
}
/*
google
aBc,bAd
*/

 

E:思维+map

 

思路:我们如果直接去枚举三个点,会超时。所以我们枚举这个转折点,然后判断有没有两个点到他的距离相等。怎么判断呢?我们可以用map<double,int>保存到当前转折点距离为d的点有几个,对于每个转折点扫一遍所有点就行了。

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1005;
int n;
pair<int, int> a[MAXN];
double dis(int i, int j)
{
    return sqrt(pow(a[i].first - a[j].first, 2) + pow(a[i].second - a[j].second, 2) );
}
int main()
{
    ios::sync_with_stdio(false);
    int T; cin >> T;
    while (T--)
    {
        cin >> n;
        for (int i = 0; i < n; i++) cin >> a[i].first >> a[i].second;
        int ans = 0;
        for (int i = 0; i < n; i++)
        {
            map<double, int> M;
            for (int j = 0; j < n; j++)
            {
                if (i == j) continue;
                double t = dis(i, j);
                if (!M.count(t)) M[t] = 1;
                else
                {
                    M[t]++;
                    ans += M[t];
                }
            }
        }
        if (ans == 0) cout << "WA" << endl;
        else cout << ans << endl;
    }
    return 0;
}
/*
2
2
1 0
0 1
3
1 0
0 1
0 0
*/

 

F:数学推导+约数个数定理

 

思路:原式同分得nx+ny=xy,化为 -nx-ny+xy=0=,两边同时加n^2得 n^2-nx-ny+xy=n^2  =》  (n-x)(n-y)=n^2,所以该问题就变为求n^2的因子个数,所以直接上约数个数定理即可,不会的小朋友点这里点击打开链接

真是太厉害了,谁知道大神们都是怎么想到的呢?感觉到被智商压制,,,

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1005;
long long n;
long long get_num(long long n)
{
    long long cnt = 0, i = 2, ans = 1;
    while (n != 1)
    {
        while (n%i == 0)
        {
            n /= i;
            cnt++;
        }
        if (cnt != 0)ans *= (cnt + 1);
        i++; cnt = 0;
    }
    return ans;
}
int main()
{
    ios::sync_with_stdio(false);
    int T; cin >> T;
    while (T--)
    {
        cin >> n;
        cout << (get_num(n*n) + 1) / 2 << endl;
    }
    return 0;
}
/*
3
1
20180101
1000000000
*/

 

G:模拟矩阵旋转

 

思路:n*m矩阵右旋公式,【x,y】=》【y,n-i-1】,矩阵赋值可以直接利用swap。每次n,m都会互换,最后记得补'/0'。

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 35;
int n, m;
char a[MAXN][MAXN], temp[MAXN][MAXN];
void Right(int n, int m)
{
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < m; j++)
        {
            if (a[i][j] == '-') temp[j][n - i - 1] = '|';
            else if(a[i][j] == '|') temp[j][n - i - 1] = '-';
            else temp[j][n - i - 1] = a[i][j];
        }
    }
    swap(a, temp);
}
int main()
{
    int T; scanf("%d", &T);
    while (T--)
    {
        scanf("%d%d", &n, &m);
        getchar();
        for (int i = 0; i < n; i++) scanf("%s", a[i]);
        string op; cin >> op;
        int cnt = 0;
        for (int i = 0; i < op.size(); i++)
        {
            if (op[i] == 'L') cnt--;
            else if (op[i] == 'R') cnt++;
        }
        cnt = (cnt + 4 * op.size()) % 4;
        //cnt %= 4; cnt += 4; cnt %= 4;
        for (int i = 0; i < cnt; i++)
        {
            Right(n, m);
            swap(n, m);
        }
        printf("%d %d\n", n, m);
        for (int i = 0; i < n; i++)
        {
            a[i][m] = '\0';
            printf("%s\n", a[i]);
        }
        printf("\n");
    }
    return 0;
}
/*
2
2 3
+-+
|+|
LLRRR

3 2
-+
+|
-+
LLL
*/

 

I:填空题,直接输出"ac"

 

 

J:思维题

思路:因为每次要加n-1个数字,所以每次一定有最小值。

设step: 总操作步数,ans:最终序列的元素,min:序列里的最小值 ,n:序列长度,sum:原序列之和。

ans = min+step

ans*n = sum+(n-1)*step

两个未知数两个方程解方程即可。

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 100005;
const int INF = 0x3f3f3f3f;
int main()
{
    int T, n; scanf("%d", &T);
    while (T--)
    {
        scanf("%d", &n);
        int MAX = 0, MIN = INF, sum = 0;
        for (int i = 0; i < n; i++)
        {
            int t; scanf("%d", &t);
            sum += t;
            MAX = max(MAX, t);
            MIN = min(MIN, t);
        }
        sum -= MIN * n;
        printf("%d %d\n", sum, MIN + sum);
    }
    return 0;
}
/*
1
3
1 2 3
*/

 

K:模拟

 

思路:直接模拟,或者找规律。模拟的时候注意一下n=1的情况。

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1e5 + 5;
int n;
string str, ans[MAXN];
int main()
{
    ios::sync_with_stdio(false);
    int T; cin >> T;
    while (T--)
    {
        cin >> n >> str;
        if (n == 1) { cout << str << endl; continue; }
        int len = str.size(), tot = 0, x = 1;
        while (tot < len)
        {
            while (x < n && tot < len) ans[x++] += str[tot++];
            //if (x == n && tot < len) ans[x--] += str[tot++];

            while (x > 1 && tot < len) ans[x--] += str[tot++];
            //if (x == 1 && tot < len) ans[x++] += str[tot++];
        }
        for (int i = 1; i <= n; i++)
        {
            cout << ans[i];
            ans[i].clear();
        }
        cout << endl;
    }
    return 0;
}
/*
100
3
ABABCADCE
1
ABABCADCE
*/

 

L:取log || 快速幂取模 || 分解质因数 || 放缩

 

取log:log(x^a)=log(y^b) =》a*log(x) = b*log(y),注意精度选取就好了。

#include<bits/stdc++.h>
using namespace std;
const double eps = 1e-5;
int x, a, y, b;
int main()
{
    ios::sync_with_stdio(false);
    int T; cin >> T;
    while (T--)
    {
        cin >> x >> a >> y >> b;
        if (abs(a*log(x) - b*log(y)) <= eps) printf("Yes\n");
        else printf("No\n");
    }
    return 0;
}
/*
4
2 20 4 10
20 20 20 20
20 21 21 20
32768 32768 1048576 24576
*/

 

快速幂取模:进行快速幂取模,试几个大点的质数就能过。 感觉这个有点玄学

 

#include<bits/stdc++.h>
using namespace std;
const int mod = 1e9+7;
long long x, a, y, b;
long long Pow(long long a, long long n)
{
    if (n == 0) return 1;
    long long res = Pow(a * a % mod, n / 2);
    if (n&1) res = res * a % mod;
    return res;
}
int main()
{
    ios::sync_with_stdio(false);
    int T; cin >> T;
    while (T--)
    {
        cin >> x >> a >> y >> b;
        if (Pow(x,a) == Pow(y,b)) printf("Yes\n");
        else printf("No\n");
    }
    return 0;
}
/*
4
2 20 4 10
20 20 20 20
20 21 21 20
32768 32768 1048576 24576
*/

分解质因数:不想写了。。。

 

放缩:一个学妹教我的。稍微有一点麻烦就不讲了

#include <bits/stdc++.h>
using namespace std;
int main()
{
    int T; scanf("%d", &T);
    while (T--)
    {
        int x, a, y, b;
        bool flag = false;
        scanf("%d%d%d%d", &x, &a, &y, &b);
        for (int i = 0; i < 100; i++)
        {
            if (x == y && a == b) { flag = true; break; }
            if (a < b) { swap(a, b); swap(x, y); }
            a = a - b;
            if (y % x) break;
            y = y / x;
        }
        if (flag) printf("Yes\n");
        else printf("No\n");
    }
    return 0;
}
/*
4
2 20 4 10
20 20 20 20
20 21 21 20
32768 32768 1048576 24576
*/

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值