【解题报告】Educational Codeforces Round 16

题目链接


A. King Moves

思路

根据棋盘的特点可以分类讨论:

  • 当王在棋盘的角落上时,王有 3 种走法。
  • 当王在棋盘的边缘上时,王有 5 种走法。
  • 其它情况,王有 8 种走法

代码

#include <bits/stdc++.h>
using namespace std;

string s;

int main() {
    cin >> s;
    if(s == "a1" || s == "h1" || s == "a8" || s == "h8") {
        cout << 3;
    }
    else if(s[0] == 'a' || s[0] == 'h' || s[1] == '1' || s[1] == '8') {
        cout << 5;
    }
    else {
        cout << 8;
    }
    cout << endl;
    return 0;
}

B. Optimal Point on a Line

思路

朴素的思想中,枚举最优点,然后计算所有点到这个点的距离之和就能解决问题。但是 O(n2) 的时间复杂度是无法胜任本题的数据规模的。

接下来考虑让点有序是否对解题会有帮助,假设现在的点是按照坐标的大小从小到大排列的。考虑当我们知道了第 i 个点到所有点的距离之和 sum(i) 后,能否知道第 i+1 个点到所有点的距离之和 sum(i+1) 。我们规定 less(i) 为比 i 坐标小的所有点的集合, dis(i,j) 为点 i 到点 j 的距离(可以用前缀和预数组处理出来)。那么点 i+1 到点集 less(i+1) 中每点的距离之和与点 i 到点集 less(i+1) 中每点的距离之和的差 sub(i) less(i+1) 的点数乘 dis(i,i+1)

于是在算出了 sum(i) ,以后,我们就可以根据 sub(i) 求得 sum(i+1) ,从而从 sum(1) 递推到 sum(n) 。问题就在 O(n) 的复杂度下圆满地解决了。

代码

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const int maxn = 3e5 + 5;
int n, x;
ll sum, ans, a[maxn];

int main() {
    scanf("%d", &n);
    for(int i = 1; i <= n; i++) {
        scanf("%I64d", &a[i]);
    }
    sort(a + 1, a + n + 1);
    for(int i = 2; i <= n; i++) {
        sum += a[i] - a[1];
    }
    ans = sum;
    x = a[1];
    for(int i = 2; i <= n; i++) {
        sum += (i + i - n - 2) * (a[i] - a[i-1]);
        if(sum < ans) {
            ans = sum;
            x = a[i];
        }
    }
    printf("%d\n", x);
    return 0;
}

C. Magic Odd Square

思路

奇数阶幻方的幻和恰好也是奇数,于是问题就转化成了如何构造奇数阶幻方。(关于幻方,幻和和奇数阶幻方的构造,进入这个链接了解更多)

这里写图片描述

代码

#include <bits/stdc++.h>
using namespace std;

const int maxn = 50;
int n, x, y, tx, ty, G[maxn][maxn];

int main() {
    scanf("%d", &n);
    x = 0;
    y = n / 2;
    for(int i = 1; i <= n * n; i++) {
        G[x][y] = i;
        tx = (x - 1 + n) % n;
        ty = (y + 1) % n;
        if(G[tx][ty] > 0) {
            x++;
            continue;
        }
        x = tx;
        y = ty;
    }
    for(int i = 0; i < n; i++) {
        for(int j = 0; j < n; j++) {
            printf("%d ", G[i][j]);
        }
        puts("");
    }
    return 0;
}

E. Generate a String

思路

原本的思路是,因为状态 i (生成 i a )能从状态 i+1 转移过来,所以用动态规划不好解决。但是用最短路可以解决。囿于题给的数据量,我没能在比赛中正确解决这个问题,最后以 MLE 告终(也有人说剪枝后能用最短路解决)。

下面说明正确的解法:

  • i 为奇数时,状态 i 从状态 i1 i+12 转移过来是最优的。为什么是 i+12 而不是 i+32 或者数值更大的状态呢?因为从 i+32 转移过来不如先从 i+32 转移到 i+321 再转移到 2×(i+321) ,即 i+1 ,这样转移到 i 的花费会更少。最后状态转移方程就是 d[i]=min(d[i1]+x,d[(i+1)/2]+x+y)
  • i 为偶数时,状态 i 从状态 i1 i2 转移过来是最优的。为什么是 i2 而不是 i+22 或数值更大的状态呢?答案跟奇数的情形是类似的,这里就不赘述了。最后状态转移方程就是 d[i]=min(d[i1]+x,d[i/2]+y)

代码

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const int maxn = 1e7 + 5;
int n, x, y;
ll d[maxn];

int main() {
    cin >> n >> x >> y;
    for(int i = 1; i <= n; i++) {
        if(i % 2) {
            d[i] = min(d[i-1] + x, d[(i+1)/2] + x + y);
        }
        else {
            d[i] = min(d[i-1] + x, d[i/2] + y);
        }
    }
    cout << d[n] << endl;
    return 0;
}

(其它题目略)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值