【解题报告】Codeforces Round #349 (Div. 2)

来源:互联网 发布:linux lldpad 编辑:程序博客网 时间:2024/06/05 16:34

题目链接


A. Pouring Rain(Codeforces 667A)

思路

首先把水位的上升和下降速度的单位统一,然后判断水位的上升和下降的速度哪个更大。最后就可以计算,输出了。

代码

#include <cstdio>#include <cmath>using namespace std;const double eps = 1e-10, pi = 4 * atan(1.0);double d, h, v, up, down;int main() {    scanf("%lf%lf%lf%lf", &d, &h, &v, &up);    down = 4 * v / pi / d / d;    if(up - down >= -eps) {        puts("NO");    }    else {        printf("YES\n%.5f\n", h / (down - up));    }    return 0;}

B. Coat of Anticubism(Codeforces 667B)

思路

先拿最简单的凸多边形——三角形来思考。假如现在有两个线段 a,b ,加入一个最小的线段 c 使得 a,b,c 能够构成三角形的约束是 a,b 中更短的线段的长度加上 c 的长度要大于 a,b 中更长的线段的长度(相当于三角形任意两边之和大于第三边)。想清楚三角形的情况后再来思考一般的凸多边形的情况。假设我们有一个线段集合 s ,通过类比不难想出,加入一个最小的线段 c 使得 s+c 能够构成凸多边形的约束是 max(s)<smax(s)+c (假设 s 中最大的线段为 max(s) )。

代码

#include <bits/stdc++.h>using namespace std;int n, a, m;long long sum;int main() {    scanf("%d", &n);    sum = m = 0;    for(int i = 0; i < n; i++) {        scanf("%d", &a);        sum += a;        m = max(m, a);    }    printf("%I64d\n", 2 * m - sum + 1);    return 0;}

C.Reberland Linguistics(Codeforces 667C)

思路

本题是关于字符串后缀的题, KMP 算法和后缀数组看上去都不会有什么用处,反倒是从尾到头搜索貌似能够得到结果,只是复杂度太高了。在搜索能够有效解决问题而复杂度太高的情况下,记忆化搜索/动态规划可能会有效。于是假设我在对字符串做从后往前的扫描,现在扫描到了字符串的第i位s,那么怎么样能够利用之前做过的工作呢?先考虑 s[i]s[i+1] 组成的长度为 2 的子串是否能别加入到题目要求我们求的后缀集合中去。显然要满足两种情况能够将这两个字符加入到后缀集合中:

  1. s[i+2],s[i+3],s[i+4] 组成的长度为 3 的子串能加入到后缀集合中去。
  2. s[i+2],s[i+3] 组成的长度为 2 的子串能加入到后缀集合中去且 s[i......i+1]!=s[i+1......i+2] (题目的要求)。

假设我们用 d[i][j] 表示以 i为首的长度为j的子串是否在后缀集合中(用 0,1 表示),那么我们可以在从右到左的扫描中按照上面的方法计算 d[i][j] 的值。当 d[i][j]1 的时候就把 s[i......i+j1] 加入到 set 中,以便以字典序的方式输出。

代码

#include <bits/stdc++.h>using namespace std;const int maxn = 5e4 + 10;int n, d[maxn][4];string s;set <string> :: iterator it;set <string> ss;int main() {    cin >> s;    n = s.size();    d[n][2] = d[n][3] = 1;    for(int i = n - 1; i >= 5; i--) {        for(int j = 2; j <= 3; j++) {            if(d[i][j] = d[i+j][j] && s.substr(i, j) != s.substr(i + j, j) || d[i+j][j^1]) {                ss.insert(s.substr(i, j));            }        }    }    cout << ss.size() << endl;    for(it = ss.begin(); it != ss.end(); it++) {        cout << *it << endl;    }    return 0;}

D. World Tour(Codeforces 667D)

思路

因为时间给了 5 秒,所以枚举其中的两个点肯定没问题,但是再多枚举一个点都是不可能的了。那么我们就枚举两个点。枚举哪两个点呢?先考虑枚举起点和终点。不过即使枚举出起点和终点,我们也很难求出中间的点,因为变化实在太多(如果只走三个点的话这样枚举或许是可行的)。然后再考虑枚举中间两个点。假设枚举到的中间两个点为 i,j ,那么如果能求出到i点距离最远的点和j点能到达的最远的点(根据题意两点之间“距离”表示的是两点之间的最短路径长度),这样枚举就是可行的。我们可以通过 BFS (不需要用 DijkstraSPFA ,因为每条边的长度都是相同的)预处理求出任意两点之间的最短路径长度。然后对于每个点得到两个序列 d1,d2d1[i] 表示所有能够到达 i 的不是i的点按照到i的距离从大到小排序得到的序列, d2[i] 表示所有从 i 出发能够到达的不是i的点按照i到它的距离从大到小排序得到的序列。那么,为什么要求出这么个序列而不是直接求距离最大的点呢?因为题目要求的 4 个点不能重复。也就是说,如果我们求出到中间两点距离最大的两个点 s,t ,那么 s,t 可能会和 i,j 发生重复。因此我们应该得到 d1,d2 序列。对于我们枚举出的点 i,j ,再枚举 d1[i]d2[j] 中的点 iijj3 个。为什么只枚举 3 个点呢?因为 i,j 是两个点。只有枚举 3 个点才能保证至少有一个点不与 i,j 重复(其实我觉得为了防止 s,t 重复至少应该枚举 4 个点,但是 3 个点也 AC 了)。最后就可以根据 iiijjj 这条路径的长度更新最长路径了。

代码

#include <bits/stdc++.h>using namespace std;typedef pair <int, int> p;const int maxn = 3010;bool vis[maxn];int mi, mj, pd, pv, nd, nv, res;int n, m, u, v, v1, v2, v3, v4, ans;int d[maxn][maxn];vector <int> G[maxn];vector <p> d1[maxn], d2[maxn];// 搜索并求最短路径长度void bfs(int s) {    queue <p> q;    q.push(p(0, s));    // 初始化访问标记    memset(vis, 0, sizeof(vis));    vis[s] = true;    while(!q.empty()) {        p node = q.front();        q.pop();        int u = node.second;        int w = node.first;        d[s][u] = w;        for(int i = 0; i < G[u].size(); i++) {            int v = G[u][i];            if(!vis[v]) {                q.push(p(w + 1, v));                // 不能取出结点的时再修改                vis[v] = true;            }        }    }}int main() {    scanf("%d%d", &n, &m);    while(m--) {        scanf("%d%d", &u, &v);        G[u].push_back(v);    }    // 预处理出每个点到其它点的最短路d[i]    for(int i = 1; i <= n; i++) {        bfs(i);    }    // 预处理出d1和d2    for(int i = 1; i <= n; i++) {        for(int j = 1; j <= n; j++) {            if(d[j][i] > 0) {                d1[i].push_back(p(d[j][i], j));            }            if(d[i][j] > 0) {                d2[i].push_back(p(d[i][j], j));            }        }        sort(d1[i].rbegin(), d1[i].rend());        sort(d2[i].rbegin(), d2[i].rend());    }    // 枚举两个中间点    for(int i = 1; i <= n; i++) {        for(int j = 1; j <= n; j++) {            if(i == j || !d[i][j]) {                continue;            }            mi = min(3, (int)d1[i].size());            mj = min(3, (int)d2[j].size());            // 枚举起点和终点            for(int ii = 0; ii < mi; ii++) {                for(int jj = 0; jj < mj; jj++) {                    p pre = d1[i][ii], nxt = d2[j][jj];                    pd = pre.first, pv = pre.second;                    nd = nxt.first, nv = nxt.second;                    if(pv == j || nv == i || pv == nv) {                        continue;                    }                    res = pd + d[i][j] + nd;                    if(res <= ans) {                        continue;                    }                    // 更新最优解                    v1 = pv, v4 = nv;                    v2 = i, v3 = j;                    ans = res;                }            }        }    }    printf("%d %d %d %d\n", v1, v2, v3, v4);    return 0;}

(其它题目略)

0 0
原创粉丝点击