The Triangle
题目传送:The Triangle
TLE代码(递归形式的DP):
#include <map>
#include <set>
#include <cmath>
#include <deque>
#include <queue>
#include <stack>
#include <cstdio>
#include <cctype>
#include <string>
#include <vector>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define LL long long
#define INF 0x7fffffff
using namespace std;
const int maxn = 105;
int a[maxn][maxn];
int n;
int max_sum(int x, int y) {
if(x == n) {
return a[x][y];
}
else {
return max(max_sum(x+1, y), max_sum(x+1, y+1)) + a[x][y];
}
}
int main() {
while(scanf("%d", &n) != EOF) {
for(int i = 1; i <= n; i ++) {
for(int j = 1; j <= i; j ++) {
scanf("%d", &a[i][j]);
}
}
cout << max_sum(1, 1) << endl;
}
return 0;
}
上面代码会超时,因为重复计算,复杂度高达O(2n),这里为了避免TLE,可以用一个辅助数组存储每一次计算出来的maxSum,因为记录了是否访问过,所以称为记忆化搜索,复杂度为O(n2)
AC代码(优化后的记忆化搜索):
#include <map>
#include <set>
#include <cmath>
#include <deque>
#include <queue>
#include <stack>
#include <cstdio>
#include <cctype>
#include <string>
#include <vector>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define LL long long
#define INF 0x7fffffff
using namespace std;
const int maxn = 105;
int maxSum[maxn][maxn];//maxSum[i][j]代表从最底部到第i层第j个可以达到的最大值
int a[maxn][maxn];
int n;
int max_sum(int x, int y) {
if(maxSum[x][y] != -1) return maxSum[x][y];//优化,记忆化搜索
if(x == n) {
maxSum[x][y] = a[x][y];
}
else {
maxSum[x][y] = max(max_sum(x+1, y), max_sum(x+1, y+1)) + a[x][y];
}
return maxSum[x][y];
}
int main() {
while(scanf("%d", &n) != EOF) {
for(int i = 1; i <= n; i ++) {
for(int j = 1; j <= i; j ++) {
scanf("%d", &a[i][j]);
maxSum[i][j] = -1;
}
}
cout << max_sum(1, 1) << endl;
}
return 0;
}
上面还是递归形式的DP,我们可以进一步改为递推形式的DP
AC代码(改为递推形式的DP):
#include <map>
#include <set>
#include <cmath>
#include <deque>
#include <queue>
#include <stack>
#include <cstdio>
#include <cctype>
#include <string>
#include <vector>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define LL long long
#define INF 0x7fffffff
using namespace std;
const int maxn = 105;
int a[maxn][maxn];
int maxSum[maxn][maxn];//maxSum[i][j]代表从最底部到第i层第j个可以达到的最大值
int n;
int main() {
while(scanf("%d", &n) != EOF) {
for(int i = 1; i <= n; i ++) {
for(int j = 1; j <= i; j ++) {
scanf("%d", &a[i][j]);
}
}
for(int i = 1; i <= n; i ++) {
maxSum[n][i] = a[n][i];
}
for(int i = n-1; i >= 1; i --) {
for(int j = 1; j <= i; j ++) {
maxSum[i][j] = max(maxSum[i+1][j], maxSum[i+1][j+1]) + a[i][j];
}
}
cout << maxSum[1][1] << endl;
}
return 0;
}
空间优化,其实可以不用maxSum数组的,因为a数组完全可以代替它
AC代码(优化了空间的DP):
#include <map>
#include <set>
#include <cmath>
#include <deque>
#include <queue>
#include <stack>
#include <cstdio>
#include <cctype>
#include <string>
#include <vector>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define LL long long
#define INF 0x7fffffff
using namespace std;
const int maxn = 105;
int a[maxn][maxn];
int n;
int main() {
while(scanf("%d", &n) != EOF) {
for(int i = 1; i <= n; i ++) {
for(int j = 1; j <= i; j ++) {
scanf("%d", &a[i][j]);
}
}
for(int i = n-1; i >= 1; i --) {
for(int j = 1; j <= i; j ++) {
a[i][j] = max(a[i+1][j], a[i+1][j+1]) + a[i][j];
}
}
cout << a[1][1] << endl;
}
return 0;
}
最长上升子序列
题目传送:2757:最长上升子序列
AC代码(复杂度O(n2)):
#include <map>
#include <set>
#include <cmath>
#include <deque>
#include <queue>
#include <stack>
#include <cstdio>
#include <cctype>
#include <string>
#include <vector>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define LL long long
#define INF 0x7fffffff
using namespace std;
const int maxn = 1005;
int n;
int a[maxn];
int maxLen[maxn];
int main() {
while(scanf("%d", &n) != EOF) {
for(int i = 0; i < n; i ++) {
scanf("%d", &a[i]);
maxLen[i] = 1;
}
int ans = 1;
for(int i = 1; i < n; i ++) {
for(int j = 0; j < i; j ++) {
if(a[i] > a[j]) {
maxLen[i] = max(maxLen[i], maxLen[j] + 1);
ans = max(ans, maxLen[i]);
}
}
}
printf("%d\n", ans);
}
return 0;
}
AC代码(复杂度O(n*logn)):
#include <map>
#include <set>
#include <cmath>
#include <deque>
#include <queue>
#include <stack>
#include <cstdio>
#include <cctype>
#include <string>
#include <vector>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define LL long long
#define INF 0x7fffffff
using namespace std;
const int maxn = 1005;
int n;
int a[maxn];
int g[maxn];
int main() {
while(scanf("%d", &n) != EOF) {
for(int i = 0; i < n; i ++) {
scanf("%d", &a[i]);
}
for(int i = 0; i < n; i ++) g[i] = INF;
int ans = 0;
for(int i = 0; i < n; i ++) {
int k = lower_bound(g, g + n, a[i]) - g;
g[k] = a[i];
ans = max(ans, k + 1);
}
printf("%d\n", ans);
}
return 0;
}
最长公共子序列
题目传送:Common Subsequence
AC代码(复杂度O(n*m)):
#include <map>
#include <set>
#include <cmath>
#include <deque>
#include <queue>
#include <stack>
#include <cstdio>
#include <cctype>
#include <string>
#include <vector>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define LL long long
#define INF 0x7fffffff
using namespace std;
char s1[1005];
char s2[1005];
int dp[1005][1005];
int main() {
while(scanf("%s %s", s1 + 1, s2 + 1) != EOF) {
int len1 = strlen(s1 + 1);
int len2 = strlen(s2 + 1);
memset(dp, 0, sizeof(dp));
for(int i = 1; i <= len1; i ++) {
for(int j = 1; j <= len2; j ++) {
if(s1[i] == s2[j]) {
dp[i][j] = dp[i-1][j-1] + 1;
}
else {
dp[i][j] = max(dp[i-1][j], dp[i][j-1]);
}
}
}
printf("%d\n", dp[len1][len2]);
}
return 0;
}
神奇的口袋
题目传送:2755:神奇的口袋
AC代码:
#include <map>
#include <set>
#include <cmath>
#include <deque>
#include <queue>
#include <stack>
#include <cstdio>
#include <cctype>
#include <string>
#include <vector>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define LL long long
#define INF 0x7fffffff
using namespace std;
int n;
int a[25];
int dp[45];//dp[i]表示有多少种不同的选择物品的方式可以达到i
int main() {
while(scanf("%d", &n) != EOF) {
for(int i = 0; i < n; i ++) {
scanf("%d", &a[i]);
}
memset(dp, 0, sizeof(dp));
dp[0] = 1;//什么都不选的时候也是一种方式
for(int j = 0; j < n; j ++) {
for(int i = 40; i >= 0; i --) {//为了保持当前状态只受上一状态影响,所以使用逆序
dp[i] += dp[i - a[j]];//往前累加所以可能情况
}
}
printf("%d\n", dp[40]);
}
return 0;
}
0-1背包问题
题目传送:Charm Bracelet
AC代码:
#include <map>
#include <set>
#include <cmath>
#include <deque>
#include <queue>
#include <stack>
#include <cstdio>
#include <cctype>
#include <string>
#include <vector>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define LL long long
#define INF 0x7fffffff
using namespace std;
int dp[13000];
int n, m;
int w[3500], d[3500];
int main() {
while(scanf("%d %d", &n, &m) != EOF) {
for(int i = 0; i < n; i ++) {
scanf("%d %d", &w[i], &d[i]);
}
memset(dp, 0, sizeof(dp));
for(int i = 0; i < n; i ++) {
for(int j = m; j >= w[i]; j --) {
dp[j] = max(dp[j], dp[j-w[i]]+d[i]);
}
}
printf("%d\n", dp[m]);
}
return 0;
}
滑雪
题目传送:1088:滑雪
AC代码:
#include <map>
#include <set>
#include <cmath>
#include <deque>
#include <queue>
#include <stack>
#include <cstdio>
#include <cctype>
#include <string>
#include <vector>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define LL long long
#define INF 0x7fffffff
using namespace std;
const int dx[] = {-1, 0, 1, 0};
const int dy[] = {0, 1, 0, -1};
int r, c;
int dp[105][105];
int h[105][105];
struct Pos {
int x, y;
int h;
bool operator < (const Pos a)const {
return h < a.h;
}
}p[10005];
int cnt;
/*
bool operator < (const Pos a, const Pos b) {
return a.h < b.h;
}
运算符重载函数,用于结构体的比较,可以写在结构体里面,也可以写外面,不过注意两种写法的不同
*/
/*
bool cmp(Pos a, Pos b) {
return a.h < b.h;
}
也用于结构体的比较,不过调用sort函数时要改一下写法,本题可以写成sort(p, p + r * c, cmp);
*/
int main() {
while(scanf("%d %d", &r, &c) != EOF) {
cnt = 0;
for(int i = 1; i <= r; i ++) {
for(int j = 1; j <= c; j ++) {
scanf("%d", &h[i][j]);
p[cnt].x = i;
p[cnt].y = j;
p[cnt ++].h = h[i][j];
dp[i][j] = 1;
}
}
sort(p, p + r * c);
int ans = 0;
for(int i = 0; i < r * c; i ++) {
int x = p[i].x;
int y = p[i].y;
for(int j = 0; j < 4; j ++) {
int xx = x + dx[j];
int yy = y + dy[j];
if(xx >= 1 && xx <= r && yy >= 1 && yy <= c && h[xx][yy] < h[x][y]) {
dp[x][y] = max(dp[x][y], dp[xx][yy] + 1);
}
}
ans = max(ans, dp[x][y]);
}
printf("%d\n", ans);
}
return 0;
}