文章目录
有数字三角形模型、LIS、LCS、走网格、最大子矩阵
1258 【例9.2】数字金字塔
数字三角形模型
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1005;
int a[N][N], dp[N][N];
int main(void)
{
int r;
cin >> r;
for (int i = 1; i <= r; i++)
for (int j = 1; j <= i; j++)
cin >> a[i][j];
for (int i = 1; i <= r; i++)
dp[i][1] = dp[i - 1][1] + a[i][1];
for (int i = 1; i <= r; i++)
dp[i][i] = dp[i - 1][i - 1] + a[i][i];
for (int i = 3; i <= r; i++)
for (int j = 2; j < i; j++)
dp[i][j] = max(dp[i - 1][j - 1], dp[i - 1][j]) + a[i][j];
int res = 0;
for (int j = 1; j <= r; j++)
res = max(res, dp[r][j]);
cout << res << endl;
return 0;
}
1259:【例9.3】求最长不下降序列
因为需要记录路径,我的办法是用一个数组来记录前驱,存入一个栈
#include <bits/stdc++.h>
using namespace std;
const int N = 205;
int arr[N], dp[N], pri[N]; // pri记录前驱
int main(void)
{
int n;
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> arr[i];
dp[i] = 1;
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j < i; j++) {
// 记录前驱
if (arr[i] >= arr[j] && dp[i] < dp[j] + 1) {
pri[i] = j;
dp[i] = max(dp[i], dp[j] + 1);
}
}
}
int res = 0, st;
for (int i = 1; i <= n; i++) {
if (res < dp[i]) {
st = i;
}
res = max(res, dp[i]);
}
cout << "max=" << res << endl;
stack<int> stk;
for (int i = st; i != 0; i = pri[i]) {
stk.push(arr[i]);
}
while (stk.size()) {
cout << stk.top() << " ";
stk.pop();
}
cout << endl;
return 0;
}
1260:【例9.4】拦截导弹(Noip1999)
注意最后会读入一个EOF,所以 n 需要减1
贪心的思路,一开始没想到😂
#include <bits/stdc++.h>
using namespace std;
const int N = 1005;
int arr[N], dp[N]; // pri记录前驱
int h[N];
int main(void)
{
int n = 0;
while (cin >> arr[++n]) {
dp[n] = 1;
}
n--;
/*
for (int i = 1; i <= n; i++) {
cout << arr[i] << " ";
}
cout << endl;*/
for (int i = 1; i <= n; i++) {
for (int j = 1; j < i; j++) {
if (arr[i] <= arr[j])
dp[i] = max(dp[i], dp[j] + 1);
}
}
int res = 0;
for (int i = 1; i <= n; i++) {
res = max(res, dp[i]);
}
cout << res << endl;
int cnt = 0, idx, minv;
for (int i = 1; i <= n; i++) {
idx = -1;
minv = 0x3f3f3f3f;
for (int j = 0; j < cnt; j++) {
if (h[j] >= arr[i] && minv > h[j]) {
minv = h[j];
idx = j;
}
}
if (idx == -1) {
h[cnt++] = arr[i];
} else {
h[idx] = arr[i];
}
}
cout << cnt << endl;
return 0;
}
1261:【例9.5】城市交通路网
dp[i]:表示以 i 结束时的最短路径
LIS,需要记录路径,类似于后面的挖地雷
#include <bits/stdc++.h>
using namespace std;
const int N = 1005;
int arr[N][N], dp[N], n, pri[N]; // 到 i 的最大数目
int main(void)
{
cin >> n;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
cin >> arr[i][j];
}
}
dp[1] = 0; // 注意这里的初始状态 /(ㄒoㄒ)/~~
for (int i = 2; i <= n; i++) {
dp[i] = 0x3f3f3f3f;
for (int j = 1; j < i; j++) {
if (arr[j][i] > 0 && dp[i] > dp[j] + arr[j][i]) {
dp[i] = min(dp[i], dp[j] + arr[j][i]);
pri[i] = j;
}
}
}
cout << "minlong=" << dp[n] << endl;
stack<int> stk;
for (int i = n; i != 0; i = pri[i]) {
stk.push(i);
}
while (stk.size() != 1) {
cout << stk.top() << " ";
stk.pop();
}
cout << stk.top() << endl;
return 0;
}
1262:【例9.6】挖地雷
dp[i]:表示以 i 结束时的最大地雷数
一开始用dfs写的,没写成,dfs(i):表示从 i 到最后的最大地雷数
一点做题感悟:
一般dp都是利用前 i 个来表示状态
而dfs一般是采用递归的形式,利用从 i 到最后来表示状态
#include <bits/stdc++.h>
using namespace std;
const int N = 205;
int arr[N], dp[N], n, pri[N]; // 到 i 的最大数目
bool g[N][N];
int main(void)
{
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> arr[i];
}
int a, b;
while (cin >> a >> b && a) {
g[a][b] = 1;
}
for (int i = 1; i <= n; i++) {
dp[i] = arr[i];
for (int j = 1; j < i; j++) {
if (g[j][i] == 1 && dp[i] < dp[j] + arr[i]) {
dp[i] = max(dp[i], dp[j] + arr[i]);
pri[i] = j;
}
}
}
int res = 0, ed;
for (int i = 1; i <= n; i++) {
if (res < dp[i]) {
ed = i;
res = dp[i];
}
}
stack<int> stk;
for (int i = ed; i != 0; i = pri[i]) {
stk.push(i);
}
while (stk.size() != 1) {
cout << stk.top() << "-";
stk.pop();
}
cout << stk.top() << endl;
cout << res << endl;
return 0;
}
1263:【例9.7】友好城市
因为需要两两之间的线路不能相交,所以可以先按一侧的坐标进行从小到大排序
再对另一侧计算LIS,最大的长度就是答案
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 5e3 + 5;
typedef pair<int, int> PII;
#define x first
#define y second
PII arr[N];
int dp[N];
int main(void)
{
int n;
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> arr[i].x >> arr[i].y;
}
sort(arr + 1, arr + n + 1);
for (int i = 1; i <= n; i++) {
dp[i] = 1;
for (int j = 1; j < i; j++) {
if (arr[i].y > arr[j].y)
dp[i] = max(dp[i], dp[j] + 1);
}
}
int res = 0;
for (int i = 1; i <= n; i++) {
res = max(res, dp[i]);
}
cout << res << endl;
return 0;
}
1264:【例9.8】合唱队形
注意dp2需要倒序进行,如果正序的话表示的是从前到后,例如dp2[i]:表示的是前 i 个数字
逆序的话才表示的是从后到前,例如dp2[i]:表示的是从 i 到 n
#include <bits/stdc++.h>
using namespace std;
const int N = 1005;
int arr[N], dp1[N], dp2[N];
int main(void)
{
int n;
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> arr[i];
dp1[i] = dp2[i] = 1;
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j < i; j++) {
if (arr[i] > arr[j]) {
dp1[i] = max(dp1[i], dp1[j] + 1);
}
}
}
// 注意这里需要倒序
for (int i = n; i >= 1; i--) {
for (int j = n; j > i; j--) {
if (arr[i] > arr[j]) {
dp2[i] = max(dp2[i], dp2[j] + 1);
}
}
}
int res = 0;
for (int i = 1; i <= n; i++) {
res = max(res, dp1[i] + dp2[i]);
}
res = n + 1 - res;
cout << res << endl;
return 0;
}
1265:【例9.9】最长公共子序列
LCS模型
#include <bits/stdc++.h>
using namespace std;
const int N = 1005;
char s[N], t[N];
int dp[N][N];
int main(void)
{
int n, m;
cin >> s + 1 >> t + 1;
n = strlen(s + 1), m = strlen(t + 1);
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
dp[i][j] = max(dp[i][j - 1], dp[i - 1][j]);
if (s[i] == t[j]) {
dp[i][j] = max(dp[i][j], dp[i - 1][j - 1] + 1);
}
}
}
cout << dp[n][m] << endl;
return 0;
}
1281:最长上升子序列
LIS模型
#include <bits/stdc++.h>
using namespace std;
const int N = 1005;
int arr[N], dp[N];
int main(void)
{
int n;
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> arr[i];
dp[i] = 1;
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j < i; j++) {
if (arr[i] > arr[j]) {
dp[i] = max(dp[i], dp[j] + 1);
}
}
}
int res = 0;
for (int i = 1; i <= n; i++) {
res = max(res, dp[i]);
}
cout << res << endl;
return 0;
}
1282:最大子矩阵
注意子矩阵是非空的
#include <bits/stdc++.h>
using namespace std;
const int N = 105;
int arr[N][N], res = -0x3f3f3f3f;
int main(void)
{
int n;
cin >> n;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
cin >> arr[i][j];
arr[i][j] += arr[i - 1][j] + arr[i][j - 1] - arr[i - 1][j - 1];
}
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
for (int k = 0; k < i; k++) { // 非空矩阵
for (int l = 0; l < j; l++) {
res = max(res, arr[i][j] - arr[k][j] - arr[i][l] + arr[k][l]);
}
}
}
}
cout << res << endl;
return 0;
}
1283:登山
类似于前面的合唱队列
#include <bits/stdc++.h>
using namespace std;
const int N = 1005;
int arr[N], dp1[N], dp2[N];
int main(void)
{
int n;
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> arr[i];
dp1[i] = dp2[i] = 1;
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j < i; j++) {
if (arr[i] > arr[j]) {
dp1[i] = max(dp1[i], dp1[j] + 1);
}
}
}
// 注意这里需要倒序
for (int i = n; i >= 1; i--) {
for (int j = n; j > i; j--) {
if (arr[i] > arr[j]) {
dp2[i] = max(dp2[i], dp2[j] + 1);
}
}
}
int res = 0;
for (int i = 1; i <= n; i++) {
res = max(res, dp1[i] + dp2[i]);
}
res--;
cout << res << endl;
return 0;
}
1284:摘花生
走网格模型
#include <bits/stdc++.h>
using namespace std;
const int N = 1005;
int arr[N][N], dp[N][N];
int main(void)
{
int t, c, r;
cin >> t;
while (t--) {
cin >> c >> r;
for (int i = 1; i <= c; i++) {
for (int j = 1; j <= r; j++) {
scanf("%d", &arr[i][j]);
}
}
for (int i = 1; i <= max(c, r); i++) {
dp[1][i] = arr[1][i] + dp[1][i - 1];
dp[i][1] = arr[i][1] + dp[i - 1][1];
}
for (int i = 2; i <= c; i++) {
for (int j = 2; j <= r; j++) {
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]) + arr[i][j];
}
}
cout << dp[c][r] << endl;
}
return 0;
}
1285:最大上升子序列和
LIS模型
#include <bits/stdc++.h>
using namespace std;
const int N = 1005;
int arr[N], dp[N];
int main(void)
{
int n;
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> arr[i];
dp[i] = arr[i];
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j < i; j++) {
if (arr[i] > arr[j]) {
dp[i] = max(dp[i], dp[j] + arr[i]);
}
}
}
int res = 0;
for (int i = 1; i <= n; i++) {
res = max(res, dp[i]);
}
cout << res << endl;
return 0;
}
1286:怪盗基德的滑翔翼
#include <bits/stdc++.h>
using namespace std;
const int N = 105;
int arr[N], dp1[N], dp2[N];
int main(void)
{
int t, n;
cin >> t;
while (t--) {
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> arr[i];
dp1[i] = dp2[i] = 1;
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j < i; j++) {
if (arr[i] > arr[j]) {
dp1[i] = max(dp1[i], dp1[j] + 1);
}
}
}
for (int i = n; i >= 1; i--) {
for (int j = n; j > i; j--) {
if (arr[j] < arr[i]) {
dp2[i] = max(dp2[i], dp2[j] + 1);
}
}
}
int res = 0;
for (int i = 1; i <= n; i++) {
res = max(res, dp1[i]);
res = max(res, dp2[i]);
}
cout << res << endl;
}
return 0;
}
1287:最低通行费
#include <bits/stdc++.h>
using namespace std;
const int N = 105;
int arr[N][N], dp[N][N];
int main(void)
{
int n;
cin >> n;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
scanf("%d", &arr[i][j]);
}
}
for (int i = 1; i <= n; i++) {
dp[1][i] = arr[1][i] + dp[1][i - 1];
dp[i][1] = arr[i][1] + dp[i - 1][1];
}
for (int i = 2; i <= n; i++) {
for (int j = 2; j <= n; j++) {
dp[i][j] = min(dp[i - 1][j], dp[i][j - 1]) + arr[i][j];
}
}
cout << dp[n][n] << endl;
return 0;
}
1288:三角形最佳路径问题
同第一题的数字金字塔
#include <bits/stdc++.h>
using namespace std;
const int N = 105;
int arr[N][N], dp[N][N];
int main(void)
{
int n;
cin >> n;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= i; j++) {
cin >> arr[i][j];
}
}
for (int i = 1; i <= n; i++) {
dp[n][i] = arr[n][i];
}
for (int i = n - 1; i >= 1; i--) {
for (int j = 1; j <= i; j++) {
dp[i][j] = max(dp[i + 1][j], dp[i + 1][j + 1]) + arr[i][j];
}
}
cout << dp[1][1] << endl;
return 0;
}
1289:拦截导弹
#include <bits/stdc++.h>
using namespace std;
const int N = 15;
int arr[N], dp[N];
int main(void)
{
int n;
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> arr[i];
dp[i] = 1;
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j < i; j++) {
if (arr[i] <= arr[j]) {
dp[i] = max(dp[i], dp[j] + 1);
}
}
}
int res = 0;
for (int i = 1; i <= n; i++) {
res = max(res, dp[i]);
}
cout << res << endl;
return 0;
}