目录
第一题:天使果冻
题目描述
输入描述:
第二题:dd爱旋转
题目描述
输入描述
输出描述
输入
输出
第三题:小红取数
描述
输入描述
输出描述
示例1
第一题:天使果冻
题目链接:天使果冻
题目描述
有 n 个果冻排成一排。第 i 个果冻的美味度是 ai。
天使非常喜欢吃果冻,但她想把最好吃的果冻留到最后收藏。天使想知道前 x 个果冻中,美味度第二大的果冻有多少美味度?
一共有 q 次询问。
注:如果最大的数有两个以上,默认第二大的等于最大的。例如, [2,3,4,2,4] 这个序列,第二大的数是4。
输入描述:
第一行一个正整数 n 。
第二行 n 个正整数 ai,用空格隔开。
第三行一个正整数 q 。
接下来的 q行,每行一个正整数 x ,代表一次询问。
数据范围:1≤q≤1e5,1≤ai≤1e9,2≤x≤n≤1e5
找一堆数中的第二大:在a,b,c,d序列中,原先最大值为f,次大值为g,这时插入一个数字x,有三种情况。
① --------------x----------g-------------f------------> 最大值f和次大值在x插入后无影响
② -------------------------g------x------f------------> 最大值f无影响,次大值变为x
③ -------------------------g-------------f-------x-----> 最大值变为x,次大值变为f
用两个数组来维护前 i 个数中的最大值和次大值,这样,每次在插入新的值的时候就可以迅速判断是否需要更新最大值和次大值。
#include <iostream>
using namespace std;const int N = 1e5 + 10;
int a[N];
int f[N], g[N];int main()
{int n;cin >> n;for (int i = 1; i <= n; i++) cin >> a[i];f[1] = a[1];for (int i = 2; i <= n; i++) {int x = a[i];f[i] = max(f[i - 1], x);if (x >= f[i - 1]) g[i] = f[i - 1];else if (x >= g[i - 1]) g[i] = x;else g[i] = g[i - 1];}int q;cin >> q;while(q--) {int x;cin >> x;cout << g[x] << endl;}return 0;
}
第二题:dd爱旋转
题目链接:dd爱旋转
题目描述
读入一个n∗n的矩阵,对于一个矩阵有以下两种操作
1:顺时针旋180°
2:关于行镜像
输入描述
第一行一个数n(1≤n≤1000),表示矩阵大小
接下来n行,每行n个数,描述矩阵,其中数字范围为[1,2000]
一下来一行一个数q(1≤q≤100000),表示询问次数
接下来q行,每行一个数x(x=1或x=2),描述每次询问
输出描述
n行,每行n个数,描述操作后的矩阵
输入
2
1 2
3 4
1
1
输出
4 3
2 1
把180度旋转拆分为一个行镜像+一个列镜像。先进行行镜像还是列镜像都无所谓,结果一样。然后,统计一共要进行多少次行镜像和列镜像。因为行镜像过去在行镜像回来和原来一样,所以%2得到需要几次行镜像,列也是同理。
行镜像:把第一行和最后一行交换,第二行和倒数第二行交换……
列镜像:把第一列和最后一列交换,第二列和倒数第二列交换……
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;int arr[1010][1010];
int n;void setRow() {for (int i = 0; i < n / 2; i++) {for (int j = 0; j < n; j++) {swap(arr[i][j], arr[n - i - 1][j]);}}
}
void setCol() {for (int j = 0; j < n / 2; j++) {for (int i = 0; i < n; i++) {swap(arr[i][j], arr[i][n - j - 1]);}}
}int main()
{cin >> n;for (int i = 0; i < n; i++) {for (int j = 0; j < n; j++) {cin >> arr[i][j];}}int q, row = 0, col = 0;cin >> q;while (q--) {int x;cin >> x;if (x == 1) col++;row++;}row %= 2;col %= 2;if (row != 0) setRow();if (col != 0) setCol();for (int i = 0; i < n; i++) {for (int j = 0; j < n; j++) {cout << arr[i][j] << " ";}cout << endl;}return 0;
}
第三题:小红取数
题目链接:小红取数
描述
小红拿到了一个数组,她想取一些数使得取的数之和尽可能大,但要求这个和必须是 𝑘 的倍数。
你能帮帮她吗?
输入描述
第一行输入两个正整数 𝑛 和 k
第二行输入 n 个正整数 ai
1≤𝑛,𝑘≤10^3
1≤𝑎𝑖≤10^10
输出描述
如果没有合法方案,输出 -1。
否则输出最大的和。
示例1
输入:
5 5
4 8 2 9 1
输出:
20
说明:
取后四个数即可.
01背包。
动态规划五部曲:
1)状态表示:dp[ i ][ j ]表示在前 i 个数中选,总和%k为 j 时的最大和。
2)状态转移方程:dp[ i ][ j ] = max( dp[ i - 1][ j ], dp[ i - 1][ (j - a[ i ] % k + k) % k] )。
3)初始化:(0, 0)位置,在前0个数中选,总和%k为0的最大和为0,其余(0,1),(0,2)等第一行位置非法,初始化为正无穷。
4)填表顺序:上到下,左到右。
5)返回值:dp[ n ][ 0 ]。
状态方程的推导过程:
1)i 位置不选,那就是去前 i - 1个数中找 %k 为 j 的最大和。
2)i 位置选,要去前 i - 1个位置中找一个总和为sum并且加上 i 位置的值 a[ i ]后,%k为 j 。这时就有一个等式:
(sum + a [ i ])% k = j --------> sum % k = j - a [ i ] % k,这就是要找的sum满足的条件。
因为 j - a [ i ] % k 可能为负数,所以加上k后可以矫正为正,但是如果式子 j - a [ i ] % k的结果就是正数,不需要矫正,那么加k后就会有问题,所以需要再%k。
#include <iostream>
#include <cstring>
using namespace std;typedef long long LL;
const int N = 1e3 + 10;
const int INF = 0x3f3f3f3f;
LL a[N];
LL dp[N][N];//从前i个数中取,%k为j的最大和int main()
{int n, k;cin >> n >> k;memset(dp, -INF, sizeof(dp));dp[0][0] = 0;for (int i = 1; i <= n; i++) cin >> a[i];for (int i = 1; i <= n; i++) {for (int j = 0; j < k; j++) {dp[i][j] = max(dp[i - 1][j], dp[i - 1][(j - a[i] % k + k) % k] + a[i]);}}if (dp[n][0] <= 0) cout << -1 << endl;else cout << dp[n][0] << endl;return 0;
}