[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; }
是二叉搜索树吗?
先判断树:首先判断是不是只有一个点没有父亲,然后判断从这个没有父亲的点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; }
方格取数
动态规划: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; }
单词接龙
这题思路是想好了,代码实现起来太费时间,手太慢不写了。
设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的主要内容,如果未能解决你的问题,请参考以下文章