动态规划的基本模型

有数字三角形模型、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;
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值