zoukankan      html  css  js  c++  java
  • [Kick Start] 2021 Round A

    题目:2021 Round-A .

    K-Goodness String

    签到题,计算当前字符串的 K-Goodness Score ,然后与给出的 K 做差即可。

    #include <iostream>
    #include <string>
    using namespace std;
    int cnt = 1;
    int solve(const string &s, int n, int k)
    {
        int cur = 0;
        for (int i = 0; i < n / 2; i++) cur += s[i] != (s[n - i - 1]);
        return abs(k - cur);
    }
    int main()
    {
        int t, n, k;
        string str;
        cin >> t;
        while (t--)
        {
            cin >> n >> k;
            cin.ignore();
            cin >> str;
            cin.ignore();
            printf("Case #%d: %d
    ", cnt++, solve(str, n, k));
        }
    }
    

    L Shaped Plots

    L 字要求长边的长度是短边的 2 倍。短边长度至少为 2 ,长边长度至少为 4 。

    枚举每个 matrix[i, j] == 1 的点(把该位置视为 L 的拐点)求出其上下左右四个方向的 1 的长度,通过 {up, down, left, right} 4 个变量记录。

    然后对于 4 个方向,把该方向作为短边尝试一次,计算以该方向作为短边的 L 的个数。

    以下面输入为例子:

    1 0 0 0 
    1 0 0 1
    1 1 1 1
    1 0 1 0 
    1 0 1 0
    1 1 1 0
    

    在位置 (2, 2) 上,其 4 个方向的 1 的长度为 {up=1, down=4, left=3, right=1} ,那么:

    • up = 1 ,该方向不能为短边。
    • down = 4 ,以该方向为短边时,那么长边必然是 left 或者 right 。 在 down 方向上,短边长度可以为 2, 3, 4, 但 left, right 作为长边均不满「长边长度至少 4 的要求」。
    • left = 3 ,以该方向作为短边,短边长度可以为 2 或者 3,当为 2 时,可以与 down 组成 1 个 L 字。
    • right = 2 ,以该方向作为短边,长度为 2 ,可以与 down 组成 1 个 L 字。

    因此在位置 (2, 2) 上共有 2 个 L 字。

    时间复杂度 (O(n^3)) .

    #include <iostream>
    #include <vector>
    #include <array>
    using namespace std;
    #define MAXN 1001
    int nums[MAXN][MAXN] = {{0}};
    int cnt = 1;
    int rows, cols;
    array<int, 4> getFourLength(int i, int j)
    {
        int up, down, left, right;
        up = down = i, left = right = j;
        while (up >= 0      && nums[up][j])    up--;
        while (down < rows  && nums[down][j])  down++;
        while (left >= 0    && nums[i][left])  left--;
        while (right < cols && nums[i][right]) right++;
        return array<int, 4>({i - up, down - i, j - left, right - j});
    }
    int solve()
    {
        int ans = 0;
        for (int i = 0; i < rows; i++)
        {
            for (int j = 0; j < cols; j++)
            {
                if (nums[i][j] == 0) continue;
                auto [up, down, left, right] = getFourLength(i, j);
                auto calc = [&ans](int shortedge, int longedge1, int longedge2) {
                    for (int d = 2; d <= shortedge; d++)
                        ans += (longedge1 >= 2*d), ans += (longedge2 >= 2*d);
                };
                calc(up, left, right), calc(down, left, right);
                calc(left, up, down), calc(right, up, down);
            }
        }
        return ans;
    }
    int main()
    {
        int t;
        cin >> t;
        while (t--)
        {
            cin >> rows >> cols;
            for (int i = 0; i < rows; i++)
                for (int j = 0; j < cols; j++)
                    cin >> nums[i][j];
            printf("Case #%d: %d
    ", cnt++, solve());
        }
    }
    

    Rabbit House

    题目的意思就是说,每个「极高点」(盗用了函数中的极值点的概念),其周围 4 个方向的点都必须与它等高,或者比它高度小 1。

    如果只有一个最高点,那么是很简单的(一次 BFS 即可完成),但这里可能会出现多个最高点。

    这里我们采用堆思想,从堆顶得到最高点,观察其 4 个相邻点的高度,判断是否需要填充高度。若需要,那么更新这个相邻点的高度(同时记录增加了多少高度),并把这个相邻点也放入堆中。

    代码实现用了 set 取替代堆了,反正我们只要能在 (O(1)) 时间内得到最高点即可。

    时间复杂度 (O(nlog{n}), n=rc) .

    #include <iostream>
    #include <vector>
    #include <set>
    using namespace std;
    #define N 301
    int t, rows, cols;
    int graph[N][N] = {{0}};
    struct node_t
    {
        int x, y, height;
        node_t(int xx = -1, int yy = -1, int hh = 0) : x(xx), y(yy), height(hh) {}
        bool operator<(const node_t &n) const
        { return height > n.height || (height == n.height && (x < n.x || (x == n.x && y < n.y))); }
    };
    uint64_t solve(set<node_t> &s)
    {
        uint64_t ans = 0;
        static const vector<pair<int, int>> dirs = {{-1, 0}, {1, 0}, {0, 1}, {0, -1}};
        while (!s.empty())
        {
            auto [x, y, height] = *s.begin();
            s.erase(s.begin());
            for (auto &d : dirs)
            {
                int dx = x + d.first, dy = y + d.second;
                if (dx < 0 || dx >= rows || dy < 0 || dy >= cols) continue;
                if (graph[dx][dy] <= height - 2)
                {
                    ans += height - 1 - graph[dx][dy];
                    s.erase(node_t(dx, dy, graph[dx][dy]));
                    graph[dx][dy] = height - 1;
                    s.emplace(dx, dy, graph[dx][dy]);
                }
            }
        }
        return ans;
    }
    int main()
    {
        cin >> t;
        for (int k = 1; k <= t; k++)
        {
            cin >> rows >> cols;
            set<node_t> s;
            for (int i = 0; i < rows; i++)
            {
                for (int j = 0; j < cols; j++)
                {
                    cin >> graph[i][j];
                    s.emplace(i, j, graph[i][j]);
                }
            }
            printf("Case #%d: %llu
    ", k, solve(s));
        }
    }
    

    Checksum

    一个重要结论:如果某行/某列,只有一个元素被损坏,那么是可以通过该行/该列的校验和进行还原的,这种情况下不需要任何的 cost .

    我们把每行,每列都看作一个点,如果位置 (i, j) 为 -1 ,那么就把 row[i]col[j] 连接起来,这样会得到一个二分图。

    对于度为 1 的点,说明该行/列,只有 1 个 -1,那么这个数据可以通过检验和还原,不需要任何的 cost ,我们在图中去掉所有这样的边,并且去掉之后,产生的新的度为 1 的点也需要同样的操作。

    上述的性质表明,如果得到图是一颗树,那么所有的数据都能通过校验和还原。

    比如下图的边 (row[1], col[2])(row[4], col[1]) 就是这样的边。

    对于剩下的边所组成的图中,我们要去掉一些边,然后令整个图成为一棵树。显然,去除 (row[i], col[j]) 这条边时,需要耗费 B[i, j]

    我们的目的是,去除权值最小的边,得到图的一棵树。换而言之,就是求出图的最大生成树 tree 。那么去除边所使用的权值就是 sum(graph) - sum(tree) .

    代码实现

    #include <iostream>
    #include <unordered_map>
    #include <vector>
    #include <queue>
    using namespace std;
    #define N 502
    #define getrowid(r) (r)
    #define getcolid(c) (n + c)
    int nt;
    int A[N][N] = {{0}}, B[N][N] = {{0}};
    int R[N] = {0}, C[N] = {0};
    
    struct node_t
    {
        int x, y, cost;
        node_t(int xx, int yy, int cc) : x(xx), y(yy), cost(cc) {}
        bool operator<(const node_t &node) const { return cost < node.cost; }
    };
    
    int find(vector<int> &root, int x) { return root[x] == -1 ? x : root[x] = find(root, root[x]); }
    
    uint64_t solve(int n)
    {
        unordered_map<int, unordered_map<int, int>> g;
        vector<int> degree(2 * n + 1, 0);
        int rid, cid;
        for (int i = 1; i <= n; i++)
        {
            for (int j = 1; j <= n; j++)
            {
                if (A[i][j] == -1)
                {
                    rid = getrowid(i), cid = getcolid(j);
                    g[rid][cid] = g[cid][rid] = B[i][j];
                    degree[rid]++, degree[cid]++;
                }
            }
        }
        // 去除度为 1 的点和这条边
        for (int i = 1; i <= 2 * n; i++)
        {
            if (degree[i] == 1)
            {
                int vex = g[i].begin()->first;
                degree[i]--, degree[vex]--;
                g.erase(i);
                if (g.count(vex))
                {
                    g[vex].erase(i);
                    if (g[vex].empty()) g.erase(vex);
                }
            }
        }
        // 使用 并查集 + Kruskal 求最大生成树
        priority_queue<node_t> q;
        vector<int> root(2 * n + 1, -1);
        uint64_t sum = 0;
        for (auto &[x, m] : g)
            for (auto [y, c] : m)
                q.emplace(x, y, c), sum += c;
        // g 是邻接矩阵,里面有重复的边
        sum /= 2;
        // cost 计算最大生成树
        uint64_t cost = 0;
        while (!q.empty())
        {
            auto [x, y, c] = q.top();
            q.pop();
            x = find(root, x), y = find(root, y);
            if (x != y)
            {
                root[y] = x;
                cost += c;
            }
        }
        return sum - cost;
    }
    int main()
    {
        cin >> nt;
        int n;
        for (int t = 1; t <= nt; t++)
        {
            cin >> n;
            uint64_t sum = 0;
            for (int i = 1; i <= n; i++)
                for (int j = 1; j <= n; j++)
                    cin >> A[i][j];
            for (int i = 1; i <= n; i++)
                for (int j = 1; j <= n; j++)
                    cin >> B[i][j];
            for (int j = 1; j <= n; j++) cin >> R[j];
            for (int j = 1; j <= n; j++) cin >> C[j];
            printf("Case #%d: %llu
    ", t, solve(n));
        }
    }
    
  • 相关阅读:
    AOJ.866 飞越原野 (三维BFS)
    UVA.129 Krypton Factor (搜索+暴力)
    UVA.10986 Fractions Again (经典暴力)
    UVA.10305 Maximum Product (暴力)
    UVA.725 Division (暴力)
    HDU.3342 Legal or Not (拓扑排序 TopSort)
    HDU.2647 Reward(拓扑排序 TopSort)
    13 成都
    15 沈阳
    14 鞍山
  • 原文地址:https://www.cnblogs.com/sinkinben/p/14614687.html
Copyright © 2011-2022 走看看