欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

【解题报告】Codeforces Round #409 (rated, Div. 2, based on VK Cup 2017 Round 2)

程序员文章站 2022-07-12 12:31:05
...

题目链接


A. Vicious Keyboard(Codeforces 801A)

思路

本题的入手点在于,由于最多只能修改一个字符,所以可以枚举被修改的字符。
枚举被修改的字符,然后统计 VK 出现的次数,更新答案。注意还可以不是用修改的机会

代码

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

string s;
int cnt, ans;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin >> s;
    // 枚举被修改的字符
    for(int i = 0; i < s.size(); i++) {
        // 修改字符
        if(s[i] == 'V') {
            s[i] = 'K';
        }
        else {
            s[i] = 'V';
        }
        cnt = 0;
        for(int j = 0; j < s.size() - 1; j++) {
            if(s[j] == 'V' && s[j + 1] == 'K') {
                cnt++;
            }
        }
        ans = max(ans, cnt);
        // 还原被修改的字符
        if(s[i] == 'V') {
            s[i] = 'K';
        }
        else {
            s[i] = 'V';
        }
    }
    cnt = 0;
    // 统计不修改的情况
    for(int j = 0; j < s.size() - 1; j++) {
        if(s[j] == 'V' && s[j + 1] == 'K') {
            cnt++;
        }
    }
    ans = max(ans, cnt);
    cout << ans << endl;
    return 0;
}

B. Valued Keys(Codeforces 801B)

思路

本题的入手点在于寻找最特殊的构造方式。
设输入的字符串为 xz 。我们要寻找满足 f(x,y)=zy 。若 z 中某位置出现比 x 中对应位置字典序大的字符,那么就无解。否则一定有解,并且我们可以贪心地令 y=z 来构造 y

代码

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

string x, z;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin >> x >> z;
    for(int i = 0; i < x.size(); i++) {
        if(x[i] < z[i]) {
            cout << -1 << endl;
            return 0;
        }
    }
    cout << z << endl;
    return 0;
}

C. Voltage Keepsake(Codeforces 801C)

思路

本题的入手点是,寻找最值比较麻烦,但判定值是否满足条件比较容易。于是可以利用二分查找来解决问题。
首先先判断什么时候有无穷解。显然,当充电的速度大于设备的耗电速度之和的时候可以用无限地使用设备。
在剩下的有解的情况中,寻找最值是比较麻烦了,但判定某个解 t 是否满足条件还是比较容易的。假设 ok(t) 判定表示所有设备坚持使用t单位的时间是否有问题的函数,我们现在要完成这个函数的编写。显然我们可以计算出在 t 时间内所有设备的总用电量和 t 时间内设备能够充上的总电量,比较它们的大小就能完成 ok(t) 的功能了。
利用这个函数进行二分查找,不断地通过判断答案区间中点的解是否满足条件来缩小答案区间,知道最后得到问题的解。

代码

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

typedef long long ll;
const int maxn = 1e5 + 10;
const double eps = 1e-10, INF = 1e10 + 10;
int n, p, a[maxn], b[maxn];
ll sum;
double l, r, mid;

// 防止精度出错的a <= b的判断
bool LE(double a, double b) {
    return a - b < eps;
}

// 判断所有设备是否能运行时间t
bool ok(double t) {
    double timeUsed, all = 0;
    for(int i = 1; i <= n; i++) {
    // 计算单个设备需要占用的充电时间
        timeUsed = (1.0 * a[i] * t - b[i]) / (1.0 * p);
        if(timeUsed > eps) {
            all += timeUsed;
        }
    }
    return LE(all, t);
}

int main() {
    scanf("%d%d", &n, &p);
    for(int i = 1; i <= n; i++) {
        scanf("%d%d", &a[i], &b[i]);
        sum += a[i];
    }
    if(sum <= p) {
        cout << -1 << endl;
        return 0;
    }
    // 二分查找
    l = 0;
    r = INF;
    for(int i = 1; i <= 100; i++) {
        mid = (l + r) / 2;
        if(ok(mid) == true) {
            l = mid;
        }
        else {
            r = mid;
        }
    }
    printf("%.9f\n", l);
    return 0;
}

D. Volatile Kite(Codeforces 801D)

思路

本题的入手点是,所有点的可移动距离为 d ,那么所有点的移动范围为一个圆,于是可以将问题转化为静态的几何问题。

所有点可以移动到某个圆的边界和圆内的所有点所在的位置,那么请看下图:

【解题报告】Codeforces Round #409 (rated, Div. 2, based on VK Cup 2017 Round 2)

(图片来源(http://codeforces.com/blog/entry/51598))

我们现在来探究多边形不凹的条件。对于任意三个点 A,B,C ,以它们为圆心 d 为半径分别画三个圆。点 A,B,C 最远可分别移动到圆 A,B,C 上。图中的红点就是它们移动后的点。可以直观地看出,圆 B 上的红点必须离线段 AC 足够远,若圆 B 上的红点靠近到圆 AC 上的红点之间的虚线以内,那么最后生成的多边形就可能是凹的。那么,只要点 B 到直线 AC 的距离大于或等于 2d 就能保证最后生成的多变形不凹了。
因为对于任意三个点都是如此,所以只要求出任意三点 A,B,C 之间 B 到线段 AC 的最小值 Min ,令 d=Min2 就可以得到答案 d 。现在问题转化成求任意三点 A,B,CB 到线段 AC 的最小值 Min
枚举三个点肯定是会超时的,所以必须另辟蹊径。我们注意到,最后的答案一定在 A,B,C 三个点相邻的情况下求出的点到线段距离之中(这些一定是最短的)。于是我们按照顺时针顺序(或逆时针顺序)枚举点 A ,计算点到直线距离的同时更新答案即可。

代码

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

// 一些常数的定义
const double eps = 1e-8;
const double INF = 2e9 + 10;

inline int cmp(double x) {
    return x < -eps ? -1 : (x > eps);
}

// 平方及安全开方函数
inline double sqr(double x) {
    return x * x;
}
inline double Sqrt(double n) {
    return sqrt(max(0.0, n));
}

// 表示点的结构体
struct Point {
    double x, y;
    Point() {}
    Point(double x, double y): x(x), y(y) {}
    void input() {
        scanf("%lf%lf", &x, &y);
    }
    friend Point operator - (const Point& a, const Point& b) {
        return Point(a.x - b.x, a.y - b.y);
    }
    double norm() {
        return Sqrt(sqr(x) + sqr(y));
    }
};

// 计算叉积(外积)
double det(const Point& a, const Point& b) {
    return a.x * b.y - a.y * b.x;
}

// 计算点积(内积)
double dot(const Point &a, const Point& b) {
    return a.x * b.x + a.y * b.y;
}

// 计算点到线段的距离
double disPointToSegment(const Point p, const Point s, const Point t) {
    if(cmp(dot(p - s, t - s)) < 0) {
        return (p - s).norm();
    }
    if(cmp(dot(p - t, s - t)) < 0) {
        return (p - t).norm();
    }
    return fabs(det(s - p, t - p) / (s - t).norm());
}

const int maxn = 1010;
int n;
Point ps[maxn];

int main() {
    scanf("%d", &n);
    for(int i = 1; i <= n; i++) {
        ps[i].input();
    }
    ps[n + 1] = ps[1];
    ps[n + 2] = ps[2];
    double Min = INF;
    // 枚举点A
    for(int i = 1; i <= n; i++) {
        Point A = ps[i];
        Point B = ps[i + 1];
        Point C = ps[i + 2];
        double d = disPointToSegment(B, A, C);
        Min = min(Min, d);
    }
    printf("%.10f\n", Min / 2);
    return 0;
}