[Offer收割]编程练习赛33

Posted PoorLitt1eThin9

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[Offer收割]编程练习赛33相关的知识,希望对你有一定的参考价值。

矩阵游戏II

把每列的数字加起来当一行处理。因为每次操作两列,所以最后最多剩下一个负数。如果负数的个数是偶数,直接所有数字的绝对值加起来即可;若负数个数为奇数,把所有数的绝对值加起来减去其中最小的绝对值的两倍即可。

技术分享
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
int a[205][205];
int main() {
#ifndef ONLINE_JUDGE
    freopen("input.txt", "r", stdin);
#endif
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            scanf("%d", &a[i][j]);
        }
    }
    for (int i = 1; i < n; i++) {
        for (int j = 0; j < n; j++) {
            a[0][j] += a[i][j];
        }
    }
    int cnt = 0, ans = 0, min = 1000000;
    for (int i = 0; i < n; i++) {
        if (a[0][i] < 0) {
            cnt++;
            a[0][i] = -1 * a[0][i];
        }
        ans += a[0][i];
        if (a[0][i] < min) {
            min = a[0][i];
        }
    }
    if (cnt % 2 != 0) {
        ans -= 2 * min;
    }
    printf("%d\n", ans);
    return 0;
}
View Code

是二叉搜索树吗?

先判断树:首先判断是不是只有一个点没有父亲,然后判断从这个没有父亲的点dfs能否访问到其他所有的点。

二叉树:每个点最多两个儿子就可以了。

二叉搜索树:左子树最大的点小于根节点,右子树最小的点大于根节点。

技术分享
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<vector>
#include<algorithm>
using namespace std;
vector<int> G[10005];
bool f[10005];
int n, t;
int max_[10005], min_[10005];
bool erchashucuowu() {
    for (int i = 1; i <= n; i++) {
        if (G[i].size() > 2) {
            return true;
        }
    }
    return false;
}
void dfsmax(int r) {
    int ret = r;
    for (int i = 0; i < G[r].size(); i++) {
        dfsmax(G[r][i]);
        if (max_[G[r][i]] > ret) {
            ret = max_[G[r][i]];
        }
    }
    max_[r] = ret;
}
void dfsmin(int r) {
    int ret = r;
    for (int i = 0; i < G[r].size(); i++) {
        dfsmin(G[r][i]);
        if (min_[G[r][i]] < ret) {
            ret = min_[G[r][i]];
        }
    }
    min_[r] = ret;
}
bool bushisousuoshu(int r) {
    if (G[r].size() == 0) {
        return false;
    }
    if (G[r].size() == 1) {
        if (G[r][0] < r) {
            if (max_[G[r][0]] > r) {
                return true;
            }
        } else {
            if (min_[G[r][0]] < r) {
                return true;
            }
        }
        return bushisousuoshu(G[r][0]);
    }
    if (max_[G[r][0]] > r) {
        return true;
    }
    if (min_[G[r][1]] < r) {
        return true;
    }
    return (bushisousuoshu(G[r][0]) || bushisousuoshu(G[r][1]));
}
void dfs(int r) {
    printf("(");
    printf("%d", r);
    if (G[r].size() == 0) {
        printf("()()");
    }
    if (G[r].size() == 1) {
        if (G[r][0] < r) {
            dfs(G[r][0]);
            printf("()");
        } else {
            printf("()");
            dfs(G[r][0]);
        }
    }
    if (G[r].size() == 2) {
        dfs(G[r][0]);
        dfs(G[r][1]);
    }
    printf(")");
}
void jianchashu(int r) {
    if (f[r]) {
        return;
    }
    f[r] = true;
    for (int i = 0; i < G[r].size(); i++) {
        jianchashu(G[r][i]);
    }
}
bool jianchashushu() {
    for (int i = 1; i <= n; i++) {
        if (!f[i]) {
            return false;
        }
    }
    return true;
}
int main() {
#ifndef ONLINE_JUDGE
    freopen("input.txt", "r", stdin);
#endif
    scanf("%d", &t);
    while (t--) {
        scanf("%d", &n);
        for (int i = 1; i <= n; i++) {
            G[i].clear();
        }
        memset(f, true, sizeof(f));
        for (int i = 1; i < n; i++) {
            int a, b;
            scanf("%d%d", &a, &b);
            G[a].push_back(b);
            f[b] = false;
        }
        for (int i = 1; i <= n; i++) {
            sort(G[i].begin(), G[i].end());
        }
        int cnt = 0, id = -1;
        for (int i = 1; i <= n; i++) {
            if (f[i]) {
                cnt++;
                id = i;
            }
        }
        if (cnt != 1) {
            printf("ERROR1\n");
            continue;
        }
        memset(f, false, sizeof(f));
        jianchashu(id);
        if (!jianchashushu()) {
            printf("ERROR1\n");
            continue;
        }
        if (erchashucuowu()) {
            printf("ERROR2\n");
            continue;
        }
        dfsmax(id);
        dfsmin(id);
        if (bushisousuoshu(id)) {
            printf("ERROR3\n");
            continue;
        }
        dfs(id);
        printf("\n");
    }
    return 0;
}
View Code

方格取数

动态规划:dp[i][j][k]表示走到第i个斜行,两条路分别位于第i斜行的第j和第k个方格。为保证不相交,设j<k。j和k分别可能由上一行的格子向右或向下走到,共4种前状态,取其中最大值加上jk两个方格中的数字即可。

因为n最大为400,数组开全会MLE。考虑到每次状态转移的时候只会用到i-1中的值,所以可以把dp开成[2][400][400],两个数组轮换使用。

技术分享
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
int a[205][205], dp[2][405][405];
int main() {
#ifndef ONLINE_JUDGE
    freopen("input.txt", "r", stdin);
#endif
    int n;
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            scanf("%d", &a[i][j]);
        }
    }
    memset(dp, 0, sizeof(dp));
    dp[1][1][1] = a[1][1];
    for (int i = 2; i < 2 * n - 1; i++) {
        int m = i < n ?  i : 2 * n - i;
        for (int j = 1; j < m; j++) {
            for (int k = j + 1; k <= m; k++) {
                int x1, x2, y1, y2, tmp = 0, last = (i - 1) % 2;
                if (i <= n) {
                    x1 = i - j + 1;
                    y1 = 1 + j - 1;
                    x2 = i - k + 1;
                    y2 = 1 + k - 1;
                    if (dp[last][j][k] > tmp) {
                        tmp = dp[last][j][k];
                    }
                    if (dp[last][j - 1][k - 1] > tmp) {
                        tmp = dp[last][j - 1][k - 1];
                    }
                    if (dp[last][j][k - 1] > tmp) {
                        tmp = dp[last][j][k - 1];
                    }
                    if (dp[last][j - 1][k] > tmp) {
                        tmp = dp[last][j - 1][k];
                    }
                } else {
                    x1 = n - j + 1;
                    y1 = i - n + j;
                    x2 = n - k + 1;
                    y2 = i - n + k;
                    if (dp[last][j][k] > tmp) {
                        tmp = dp[last][j][k];
                    }
                    if (dp[last][j + 1][k + 1] > tmp) {
                        tmp = dp[last][j + 1][k + 1];
                    }
                    if (dp[last][j][k + 1] > tmp) {
                        tmp = dp[last][j][k + 1];
                    }
                    if (dp[last][j + 1][k] > tmp) {
                        tmp = dp[last][j + 1][k];
                    }
                }
                dp[1 - last][j][k] = tmp + a[x1][y1] + a[x2][y2];
            }
        }
    }
    printf("%d\n", dp[0][1][2] + a[n][n] * 2 + a[1][1]);
    return 0;
}
View Code

单词接龙

这题思路是想好了,代码实现起来太费时间,手太慢不写了。

设f[i]表示以第i个单词开始能往后构造的长度。

从单词s开始可以把26个字母分别加在左边或右边,共52种可能的接龙方式。从这52个f值中选一个最大的加1即可。因为顺序没法确定,所以用dfs来算:for (int i=0;i<n;i++) dfs(i);

题目中直说单词总长度小于1000000,没说每个单词最大长度,很讨厌。

每个单词的52种扩展中不一定全在字典里,所以要用一个Trie树来判断。因为单词很长,所以不能一个词一个词的分开存,只能全存在一个数组里然后记录各个端点,用的时候sscanf。

以上是关于[Offer收割]编程练习赛33的主要内容,如果未能解决你的问题,请参考以下文章

[Offer收割]编程练习赛42

hihocoder [Offer收割]编程练习赛18 C 最美和弦(dp)

[Offer收割]编程练习赛39

[Offer收割]编程练习赛32

[Offer收割]编程练习赛63

[Offer收割]编程练习赛40