Codeforces Round #462 (Div. 2) + DP
Posted zut-syp
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Codeforces Round #462 (Div. 2) + DP相关的知识,希望对你有一定的参考价值。
Codeforces链接 :http://codeforces.com/contest/934
A. A Compatible Pair(枚举)
??题意 :有两个人分别有一些数字,(Tommy) 有 (n) 个数字,(Banban) 有 (m) 个数字, 现在要求 (Tommy) 从自己的数字中去掉一个数字,(Banban) 要从自己的数字中和 (Tommy) 的数字中分别选择一个数字进行乘法运算得到 (ans);(Tommy) 的目的是让这个值尽量的小,(Banban) 的目的是让这个值尽量的大。最后问这个值最大可以是多少?
??思路 :由于数据规模比较小,所有我们对两组数据分别进行排序,然后直接枚举所有的结果即可,(Tommy) 要么删除第一个数字,这样 (Banban) 可以得到一个最大值,要么删除最后一个数字,此时 (Banban) 又可以得到这个最大值,因为 (Tommy) 想要这个值最小,所以在这两个值中选择一个最小值即可。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll inf = 1e18;
const int maxn = 100;
int a[maxn], b[maxn];
int main () {
int n, m; scanf("%d%d", &n, &m);
for (int i = 0; i < n; ++ i) scanf("%d", &a[i]);
for (int i = 0; i < m; ++ i) scanf("%d", &b[i]);
sort(a, a + n); sort(b, b + m);
ll Max = -inf;
for (int i = 1; i < n; ++ i) {
for (int j = 0; j < m; ++ j) {
Max = max(Max, 1ll * a[i] * b[j]);
}
}
ll Max2 = -inf;
for (int i = 0; i < n - 1; ++ i) {
for (int j = 0; j < m; ++ j) {
Max2 = max(Max2, 1ll * a[i] * b[j]);
}
}
printf("%lld
", min(Max, Max2));
return 0;
}
B. A Prosperous Lot(模拟)
??题意 :0 ~ 9 这些数字中含有一定数量的圆圈(0,4,6, 8,9),现在给你一个 (n),问是否有一个 (10^{18}) 以内的数包含 (n) 个圆圈。
??思路 :直接贪心的取输出 8 即可。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
//0 1 2 3 4 5 6 7 8 9
int main () {
int k; scanf("%d", &k);
if (k & 1) {
k /= 2;
if (k <= 17) {
printf("4");
for (int i = 0; i < k; ++ i) printf("8");
} else puts("-1");
} else {
k /= 2;
if (k <= 18) {
for (int i = 0; i < k; ++ i) printf("8");
} else puts("-1");
}
return 0;
}
C. A Twisty Movement(思维)
??题意 :给一个只有 1 和 2 组成的数组,我们可以反转这个数组中的某一个区间,要保证翻转之后的数组都最大不下降子序列是最长的。
??思路 :如果我们翻转某一个区间,那么我们就可以将原来的数组分成三个部分,左边的部分如果要满足题意那么统计的必定是 1 的个数,中间选中的区间我们能够选择的就是其中的最长不上升子序列(这样反转之后才能变成最长不下降的子序列),然后右边的部分就是 2 的个数,此时我们需要做的就是最大化选中的区间的最长不上升子序列的长度。我们利用 (dp) 的思想,(dp[i][j][k]) 表示在 ([i,j]) 这个区间内以 (k) 结尾的最长不上升子序列的长度,对于以 2 结尾的序列来说,那么它的前面一个状态一定也是以 2 结尾的序列,对于以 1 结尾的序列来说它可以由以 2 结尾的序列得到,也可以以 1 结尾的序列得到。这样我们就可以得出递推方程(见代码)。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e3 + 10;
int pre[maxn], suf[maxn], a[maxn];
int dp[maxn][maxn][3];
int main () {
int n; scanf("%d", &n);
for (int i = 1; i <= n; ++ i) scanf("%d", &a[i]);
for (int i = 1; i <= n; ++ i) pre[i] = pre[i - 1] + (a[i] == 1);
for (int i = n; i >= 1; -- i) suf[i] = suf[i + 1] + (a[i] == 2);
//for (int i = 1; i <= n; ++ i) cout << pre[i] << " " << suf[i] << endl;
int ans = 0;
for (int i = 1; i <= n; ++ i) {
for (int j = i; j <= n; ++ j) {
dp[i][j][2] = dp[i][j - 1][2] + (a[j] == 2);
dp[i][j][1] = max(dp[i][j - 1][1], dp[i][j - 1][2]) + (a[j] == 1);
ans = max(ans, max(dp[i][j][1], dp[i][j][2]) + pre[i - 1] + suf[j + 1]);
}
}
printf("%d
", ans);
return 0;
}
1183 编辑距离
??题目链接 :https://www.51nod.com/Challenge/Problem.html#problemId=1183
??思路 :51 nod → 学习 → 动态规划基础篇 → 编辑距离问题
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e3 + 10;
char a[maxn], b[maxn];
int dp[maxn][maxn];
int main () {
scanf("%s%s", a, b);
int lena = strlen(a), lenb = strlen(b);
for (int i = 0; i <= lena; ++ i) {
for (int j = 0; j <= lenb; ++ j) dp[i][j] = 0;
}
for (int i = 1; i <= lena; ++ i) dp[i][0] = i;
for (int j = 1; j <= lenb; ++ j) dp[0][j] = j;
for (int i = 1; i <= lena; ++ i) {
for (int j = 1; j <= lenb; ++ j) {
if (a[i - 1] == b[j - 1]) dp[i][j] = dp[i - 1][j - 1];
else {
dp[i][j] = min(dp[i - 1][j - 1] + 1, min(dp[i - 1][j] + 1, dp[i][j - 1] + 1));
}
}
}
printf("%d
", dp[lena][lenb]);
return 0;
}
1006 最长公共子序列Lcs
??题目链接 :https://www.51nod.com/Challenge/Problem.html#problemId=1006
??思路 :51 nod → 学习 → 动态规划基础篇 → 最长公共子序列问题。
//dfs
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1000;
char a[] = "adjgklsd";
char b[] = "abskdjlf";
// n = -1 或者 m = -1,则取空序列; 递归基
//a[n] = ‘X‘ = b[m] LCS(a[0, n), b[0, m)) + ‘X‘; 减而治之
//a[n] ≠ b[m],在 LCS(a[0, n], b[0, m)) 和 LCS(a[0, n), b[0, m]) 中取更长 分而治之
int lcs(int n, int m) {
if (n == -1 || m == -1) return 0;
if (a[n] == b[m]) return lcs(n - 1, m - 1) + 1;
else return max(lcs(n - 1, m), lcs(n, m - 1));
}
string Lcs(int n, int m) {
if (n == -1 || m == -1) return "";
if (a[n] == b[m]) return Lcs(n - 1, m - 1) + a[n];
else return Lcs(n - 1, m).size() > Lcs(n, m - 1).size() ? Lcs(n - 1, m) : Lcs(n, m - 1);
}
int main() {
cout << lcs(strlen(a) - 1, strlen(b) - 1) << endl;
cout << Lcs(strlen(a) - 1, strlen(b) - 1) << endl;
return 0;
}
//DP
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e3 + 10;
char a[maxn], b[maxn];
int dp[maxn][maxn], path[maxn][maxn];
void print(int i, int j) {
if (i == 0 || j == 0) return ;
if (path[i][j] == 0) {
print(i - 1, j - 1);
printf("%c", a[i - 1]);
} else if (path[i][j] == 1) {
print(i - 1, j);
} else if (path[i][j] == -1){
print(i, j - 1);
}
}
int main () {
scanf("%s%s", a, b);
int lena = strlen(a), lenb = strlen(b);
for (int i = 0; i <= lena; ++ i) {
for (int j = 0; j <= lenb; ++ j) dp[i][j] = 0;
}
for (int i = 1; i <= lena; ++ i) {
for (int j = 1; j <= lenb; ++ j) {
if (a[i - 1] == b[j - 1]) {
dp[i][j] = dp[i - 1][j - 1] + 1;
path[i][j] = 0;
} else {
if (dp[i - 1][j] > dp[i][j - 1]) {
dp[i][j] = dp[i - 1][j];
path[i][j] = 1;
} else {
dp[i][j] = dp[i][j - 1];
path[i][j] = -1;
}
}
}
}
print(lena, lenb);
puts("");
return 0;
}
//DP
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1000;
char a[] = "adjgklsds";
char b[] = "abskdjlfs";
char ans[maxn];
int dp[maxn][maxn];
// n = -1 或者 m = -1,则取空序列; 递归基
//a[n] = ‘X‘ = b[m] LCS(a[0, n), b[0, m)) + ‘X‘; 减而治之
//a[n] ≠ b[m],在 LCS(a[0, n], b[0, m)) 和 LCS(a[0, n), b[0, m]) 中取更长 分而治之
int main() {
memset(dp, 0, sizeof(dp));
int n = strlen(a), m = strlen(b);
for (int i = 0; i < n; ++ i) {
for (int j = 0; j < m; ++ j) {
if (a[i] == b[j]) dp[i + 1][j + 1] = dp[i][j] + 1;
else dp[i + 1][j + 1] = max(dp[i + 1][j], dp[i][j + 1]);
}
}
cout << dp[n][m] << endl;
int i = n, j = m, len = dp[n][m];
ans[len] = ‘ ‘;
while (i >= 1 && j >= 1) {
if (a[i - 1] == b[j - 1]) {
ans[-- len] = a[i - 1];
-- i, -- j;
} else if (dp[i][j] == dp[i - 1][j]) {
-- i;
} else -- j;
}
puts(ans);
return 0;
}
Two(Lcs 变形)
??题目连接 :http://acm.hdu.edu.cn/showproblem.php?pid=5791
??题意 :给出两个序列,让我们求出这两个序列中有多少对相同的子序列。结果对 (10^{9} + 7) 取模。
??思路 :(dp[i][j]) 长度为 (i) 的 (a) 序列和长度为 (j) 的 (b) 序列有多少对相同的序列。现在如果我们在 (b) 序列中增加一个数字,现在是 (i) 和 (j + 1) 的两个序列进行匹配在进行匹配的时候我们肯定需要算上 (i) 和 (j) 匹配的这一段,同理在 (a) 中增加一个数字也是如此,所以当 (a[i] != b[j]) 时,我们需要对 (i - 1) 和 (j) ,(i) 和 (j - 1) 进行匹配,此时这两种匹配是都对 (i - 1) 和 (j - 1) 进行了匹配,所以重复了一次也就是 (i) 和 (j) 的前缀我们匹配了两次,所以此时 (dp[i][j] = dp[i - 1][j] + dp[i][j - 1] - dp[i - 1][j - 1]),当 (a[i] == b[j]) 时,首先我们需要增加 ((a[i], b[j])) 这一对,还需要增加 (i - 1) 和 (j - 1) 这个前缀所以就有了 (dp[i][j] = d[i - 1][j] + dp[i][j - 1] - dp[i - 1][j - 1] + dp[i - 1][j - 1] + 1)。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e3 + 10;
const int mod = 1e9 + 7;
int a[maxn], b[maxn], dp[maxn][maxn];
int main () {
int n, m;
while (~ scanf("%d %d", &n, &m)) {
for (int i = 1; i <= n; ++ i) scanf("%d", &a[i]);
for (int i = 1; i <= m; ++ i) scanf("%d", &b[i]);
for (int i = 1; i <= n; ++ i) {
for (int j = 1; j <= m; ++ j) {
if (a[i] == b[j]) dp[i][j] = (dp[i][j - 1] + dp[i - 1][j] + 1) % mod;
else dp[i][j] = (dp[i][j - 1] + dp[i - 1][j] - dp[i - 1][j - 1]) % mod;
}
}
printf("%d
", (dp[n][m] + mod) % mod);
}
return 0;
}
以上是关于Codeforces Round #462 (Div. 2) + DP的主要内容,如果未能解决你的问题,请参考以下文章
Codeforces Round #462 (Div. 2) ----D
Codeforces Round #462 (Div. 1) BA Determined Cleanup
Codeforces Round #462 (Div. 1) A A Twisty Movement
Codeforces Round #436 E. Fire(背包dp+输出路径)