DP入门题

PATA1007

题目要求求出最大子序列的各元素之和,并且输出最大子序列的第一个元素和最后一个元素的值。使用一个dp数组,dp[i]表示以第i个元素为末尾的和最大的序列。由于需要用到序列的首元素,所以在DP时就要记录。状态转移方程如下:
dp[0].start=0;dp[0].v=dat[0]
dp[i1]<0,dp[i].start=i,dp[i].v=dat[i]
dp[i1]0,dp[i].start=dp[i1].start,dp[i].v=dat[i]+dp[i1].v
题目要求要求按照一定的优先级输出结果,即最大值、i、j,那么可以使用三轮遍历,也可以自定义一个比较函数使用sort(时间复杂度会增加)。
代码如下:

#include<iostream>
#include<string>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int MAX = 10010;
int K = 0;
struct node {
int start, end, v;
node(int a,int b,int c):start(a),end(b),v(c){}
node(){}
};
//dp数组,第i个元素储存以i为末尾的序列的最大值
struct node dp[MAX];
//原始data
int dat[MAX] = { 0 };
//全为负数?
bool flag = true;
void input() {
cin >> K;
for (int i = 0; i < K; i++) {
cin >> dat[i];
if (dat[i] >= 0) flag = false;
}
}
struct cmp {
bool operator()(const struct node& a, const struct node& b) {
if (a.v != b.v) return a.v > b.v;
if (a.start != b.start) return a.start < b.start;
if (a.end != b.end) return a.end < b.end;
}
};
int main(void) {
ios::sync_with_stdio(false);
input();
if (flag) {
cout << 0 << " " << dat[0] << " " << dat[K - 1] << endl;
return 0;
}
//dp计算
dp[0] = node(0, 0, dat[0]);
for (int i = 1; i < K; i++) {
int s, v;
if (dp[i - 1].v >= 0) {
s = dp[i - 1].start;
v = dp[i - 1].v + dat[i];
}
else {
s = i;
v = dat[i];
}
dp[i] = node(s, i, v);
}
//输出结果
//将结果排序
sort(dp, dp + K, cmp());
cout << dp[0].v <<" "<< dat[dp[0].start] <<" "<< dat[dp[0].end]<<endl;
}

patA1045之LIS做法

使用最长不下降子序列做法,dp[i]储存以第i个元素结尾的所有序列的最大长度,可以写出状态转移方程:
dp[i]=MAX{1,dp[j]+1},j{0...i1}ji
关键在于如何表示顺序的先后关系。代码1的思路是使用一个二维数组,[i][j]的意义即为数字j允许出现在数字i之前。但这样代码较为冗长。
代码1

#include<iostream>
#include<string>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<unordered_map>
#include<vector>
#include<set>
using namespace std;
const int MAX = 10010;
const int MAX2 = 205;
int N, M, L;
//newl储存去掉所有不喜欢的颜色后的色带元素个数
int newl;
//map[i][j]表示j可以出现在i之前
bool map1[MAX2][MAX2] = { false };
//储存去掉不喜欢颜色后的色带
vector<int> dat;
//储存喜欢的颜色
vector<int> color;
set<int> colorset;
void input() {
cin >> N;
cin >> M;
for (int i = 0; i < M; i++) {
int c; cin >> c; color.push_back(c); colorset.insert(c);
}
cin >> L;
for (int i = 0; i < L; i++) {
int c; cin >> c;
if (colorset.find(c) != colorset.end()) {
dat.push_back(c);
}
}
}
//创建映射
void createmap() {
for (int i = 0; i < color.size(); i++) {
int key = color[i];
for (int j = 0; j <= i; j++) {
map1[key][color[j]] = true;
}
}
}
int dp[MAX] = { 0 };
int main(void) {
ios::sync_with_stdio(false);
input();
createmap();
//dp求解,dp[i]储存以色带第i个元素为末尾且满足顺序的所有序列中,序列的最大长度
dp[0] = 1;
for (int i = 1; i < dat.size(); i++) {
int dpmax = 0;
for (int j = 0; j < i; j++) {
//元素j的顺序在i之前
if (map1[dat[i]][dat[j]]) {
if (dp[j] > dpmax) dpmax = dp[j];
}
}
dp[i] = (dpmax == 0 ? 1 : dpmax+1);
}
int result=*max_element(dp,dp+dat.size());
cout << result << endl;
}

代码2借鉴了教材。使用一个数组来完成数字到顺序间的映射关系。然后在dp时直接按照映射得到相对的顺序。
代码如下:

#include<iostream>
#include<string>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<unordered_map>
#include<vector>
#include<set>
using namespace std;
const int MAX = 10010;
const int MAX2 = 205;
int N, M, L;
//储存去掉不喜欢颜色后的色带
vector<int> dat;
//映射,将喜欢的颜色映射到0,1,2...
int myhash[MAX2];
void input() {
cin >> N;
cin >> M;
for (int i = 0; i < M; i++) {
int c; cin >> c;
myhash[c] = i;
}
cin >> L;
for (int i = 0; i < L; i++) {
int c; cin >> c;
if (myhash[c]!=-1) {
dat.push_back(c);
}
}
}
int dp[MAX] = { 0 };
int main(void) {
fill(myhash, myhash + MAX2, -1);
ios::sync_with_stdio(false);
input();
//dp求解,dp[i]储存以色带第i个元素为末尾且满足顺序的所有序列中,序列的最大长度
dp[0] = 1;
for (int i = 1; i < dat.size(); i++) {
int dpmax = 0;
for (int j = 0; j < i; j++) {
//元素j的顺序在i之前
if (myhash[dat[i]]>=myhash[dat[j]]) {
if (dp[j] > dpmax) dpmax = dp[j];
}
}
dp[i] = (dpmax == 0 ? 1 : dpmax+1);
}
int result=*max_element(dp,dp+dat.size());
cout << result << endl;
}

不难看出,两种写法的时间复杂度都是O(L2),这个无疑是很大的,所以不出所料这两个版本的代码在acwing的oj中都会超时,但pat的水oj可以ac。接下来介绍可以在O(ML)的时间复杂度中求解的LCS做法。


patA1045之LCS做法

LCS即最长公共子串。假设有两个字符串s1和s2分别长为M和N,s1=acb,s2=abbcccc,则最长公共子串为acccc。
使用一个dp数组dp[M+2][N+2],其中dp[i][j]表示以s1的第i位为结尾的字串和s2的第j位为结束的字串两个的最长公共子串长度。
状态转移方程为
s1[i]=s2[j],dp[i][j]=max{dp[i1][j],dp[i][j1]}+1
,dp[i][j]=max{dp[i1][j],dp[i][j1]}
代码如下:

#include<cstring>
#include<cstdio>
#include<string>
#include<iostream>
#include<vector>
using namespace std;
const int MAX1 = 205;
const int MAX2 = 10005;
int N, M, L;
int dp[MAX1][MAX2] = { 0 };
//标记第i个颜色是否为喜欢的
bool like[MAX1] = { false };
//储存去掉不喜欢的颜色后的色带
vector<int> mydata;
//储存喜欢的色带
int order[MAX1] = { 0 };
void input() {
cin >> N;
cin >> M;
for (int i = 1; i <= M; i++) {
int j; cin >> j;
like[j] = true;
order[i] = j;
}
cin >> L;
mydata.push_back(0);
for (int i = 0; i < L; i++) {
int j; cin >> j;
if (like[j]) mydata.push_back(j);
}
}
int main(void) {
ios::sync_with_stdio(false);
input();
for (int i = 1; i <= M; i++) {
for (int j = 1; j < mydata.size(); j++) {
if (order[i] == mydata[j]) {
dp[i][j]=max(dp[i-1][j],dp[i][j-1])+1;
}
else {
dp[i][j]= max(dp[i - 1][j], dp[i][j - 1]);
}
}
}
cout << dp[M][mydata.size()-1] << endl;
}

PATA1524

这道题如果用DP的方法,就是最长回文子串模型。
使用一个bool类型的二维dp数组,其中dp[i][j]表示以i、j为首尾的字串是否为回文字串
状态转移方程:
str[i]=str[j]dp[i][j]=dp[i+1][j1]
边界条件:dp[i][i]=1,dp[i][i+1]=str[i]==str[i+1]?1:0
比较重要的是它的遍历方法,不能用之前的递增i,j的方法,而是每次对一个长度的字串进行dp,每轮结束递增该长度。
代码如下:

#include<iostream>
#include<string>
#include<cstring>
#include<cstdio>
using namespace std;
const int MAX = 1005;
string str;
//dp[i][j]表示字串中第i个与第j个中间的子串是否为回文
bool dp[MAX][MAX] = { 0 };
int result=1; //最长回文子串的长度
int main(void) {
ios::sync_with_stdio(false);
//注意不能直接用cin输入给str,否则得到的是单词而不是一整行
getline(cin, str);
int len = str.length();
//初始化dp矩阵的边界值
for (int i = 0; i < len; i++) {
dp[i][i] = true;
if (str[i] == str[i + 1]) {
dp[i][i + 1] = true;
result = 2;
}
}
//dp递推
for (int l = 3; l <= len; l++) {
for (int i = 0; i <= len - l; i++) {
int begin = i;
int end = i + l - 1;
if (str[begin] == str[end]) {
if (dp[begin + 1][end - 1]) {
dp[begin][end] = true;
result = l;
}
}
}
}
cout << result << endl;
}
posted @   带带绝缘体  阅读(32)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示