数塔,LCS,LIS,LCIS,01背包
前言
这次很多水题,因为是入门DP
DP这种东西,稍微变形我就不会了 QAQ
- 数塔
https://blog.csdn.net/theonegis/article/details/45801201 - LIS
https://xuanwo.org/2015/07/31/dp-lis/ - LCIS
https://www.cnblogs.com/WArobot/p/7479431.html
https://blog.csdn.net/kanosword/article/details/51811084
免费馅饼 - HDU 1176
题目大意
(略!)
思路
数塔模型
定义状态dp[i][j]:第i秒在j位置捡到的馅饼最大数量,则状态转移方程为
dp[i + 1][j + 1] = max(dp[i][j - 1] + a[i][j - 1], dp[i][j] + a[i][j], dp[i][j + 1] + a[i][j + 1])
然后倒过来走,因为最开始的位置是知道的
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 110000;
int a[N][15];
inline int mmax(int a, int b, int c){
int p = a > b ? a : b;
int q = b > c ? b : c;
return p > q ? p : q;
}
int main(){
int n;
while(scanf("%d", &n) && n){
memset(a[0], 0, sizeof(a));
while(n--){
int x, t;
scanf("%d%d", &x, &t);
a[t][x + 1]++; //偏移一位,因为要dp的部分有j - 1,不偏移会越界
}
for(int i = N - 1; i >= 0; i--){
for(int j = 1; j <= 11; j++){
a[i][j] += mmax(a[i + 1][j - 1], a[i + 1][j], a[i + 1][j + 1]);
}
}
printf("%d\n", a[0][6]);
}
}
威威猫系列故事——打地鼠 - HDU 4540
题目大意
(继续略!)
思路
仍然是数塔模型
定义状态dp[i][j]为第i个时刻打位置为x的地鼠消耗的最小能量,则状态转移方程为
dp[i + 1][j] = min(|a[i + 1][j] - a[i][k]| + dp[i][k])
#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
const int N = 25;
const int inf = 0x3f3f3f3f;
inline int mabs(int a) { return a < 0 ? -a : a; }
inline int mmin(int a, int b) { return a < b ? a : b; }
int dp[N][N], a[N][N];
int main(){
int n, m;
while(~scanf("%d%d", &n, &m)){
memset(dp[0], 0, sizeof(dp));
for(int i = 0; i < n; i++){
for(int j = 0; j < m; j++){
scanf("%d", &a[i][j]);
}
}
for(int i = 0; i < n - 1; i++){
for(int j = 0; j < m; j++){
int ans = inf;
for(int k = 0; k < m; k++){
ans = mmin(mabs(a[i + 1][j] - a[i][k]) + dp[i][k], ans);
}
dp[i + 1][j] = ans;
}
}
int ans = inf;
for(int j = 0; j < m; j++){
ans = mmin(ans, dp[n - 1][j]);
}
printf("%d\n", ans);
}
}
Common Subsequence - HDU 1159
题目大意
求LCS
思路
LCS模板题
#include <cstdio>
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 1e3 + 15;
const int inf = 0x3f3f3f3f;
char s[N], p[N];
int dp[N][N];
inline int mmax(const int& a, const int& b) { return a > b ? a : b; }
int main()
{
while(~scanf("%s%s", s + 1, p + 1)){
memset(dp[0], 0, sizeof(dp));
int n = strlen(s + 1);
int m = strlen(p + 1);
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
if(s[i] == p[j]){
dp[i][j] = dp[i - 1][j - 1] + 1;
}else{
dp[i][j] = mmax(dp[i - 1][j], dp[i][j - 1]);
}
}
}
printf("%d\n", dp[n][m]);
}
return 0;
}
Super Jumping! Jumping! Jumping! - HDU-1087
题目大意
求严格递增子序列中和最大值
思路
LIS变形,原来LIS是DP长度的,现在DP和
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 1e3 + 15;
const int inf = 0x3f3f3f3f;
int dp[N], a[N];
inline int mmax(int a, int b) { return a > b ? a : b; }
int main()
{
int n;
while(scanf("%d", &n) && n){
memset(dp, 0, sizeof(dp));
for(int i = 0; i < n; i++){
scanf("%d", &a[i]);
}
dp[0] = a[0];
for(int i = 1; i < n; i++){
int ans = 0;
for(int j = 0; j < i; j++){
if(a[j] < a[i]) ans = mmax(ans, dp[j]);
}
dp[i] = ans + a[i];
}
int ans = 0;
for(int i = 0; i < n; i++){
ans = mmax(ans, dp[i]);
}
printf("%d\n", ans);
}
return 0;
}
FatMouse’s Speed - HDU 1160
题目大意
给定n只老鼠的质量和速度,可重新排序求出一个序列,这个序列满足质量严格递减的情况下,速度严格递增,并且是所有满足上述条件下元素个数最多的
思路
LIS变形
按质量降序排序,然后用LIS求出满足条件的递增序列,并在此过程中用pre标记是接在哪个bj后面
最后搜索最大的dp,然后通过pre输出所有元素
#include <cstdio>
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 1e3 + 15;
const int inf = 0x3f3f3f3f;
struct st{
int m, v, p;
bool operator <(const st& b){
if(m != b.m) return m > b.m;
else return v < b.v;
}
};
st a[N];
int dp[N];
int pre[N];
int main()
{
int tot = 0;
memset(pre, -1, sizeof(pre));
while(~scanf("%d%d", &a[tot].m, &a[tot].v)){
a[tot].p = tot;
tot++;
}
sort(a, a + tot);
dp[0] = 1;
for(int i = 1; i < tot; i++){
int ansl = 0, ansp = -1;
for(int j = 0; j < i; j++){
if(a[j].v < a[i].v && a[j].m > a[i].m && ansl < dp[j]){
ansl = dp[j];
ansp = j;
}
}
if(~ansp) { pre[i] = ansp; }
dp[i] = ansl + 1;
}
int ans = 0, p = 0;
for(int i = 0; i < tot; i++){
if(ans < dp[i]){
ans = dp[i];
p = i;
}
}
printf("%d\n", dp[p]);
while(~p){
printf("%d\n", a[p].p + 1);
p = pre[p];
}
return 0;
}
Constructing Roads In JGShining’s K - HDU-1025
题目大意
给定两条水平线上的2*n个点的连接情况,分别从左到右编号为1,2,…,n,求出最多数量的不相交线的集合的大小
思路
LIS变形
画图可以看出,可以把输入的两个点a b处理成road[a] = b,然后对road数组求LIS就是答案了
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 5e5 + 15;
const int inf = 0x3f3f3f3f;
int dp[N];
int road[N];
int main(){
int n;
int caseno = 1;
while(~scanf("%d", &n)){
memset(road, 0, sizeof(road));
for(int i = 1; i <= n; i++){
int a, b;
scanf("%d%d", &a, &b);
road[a] = b;
}
int mx = 0;
for(int i = 1; i <= n; i++){
int p = lower_bound(dp, dp + mx, road[i]) - dp;
dp[p] = road[i];
mx = max(mx, p + 1);
}
printf("Case %d:\n", caseno++);
if(mx <= 1){
printf("My king, at most %d road can be built.\n", mx);
}else{
printf("My king, at most %d roads can be built.\n", mx);
}
puts("");
}
}
病毒 - CSU 1120
题目大意
(略略略!)
思路
LCIS模板题
个人采用O(n*m)方法
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 1000 + 15;
const int inf = 0x3f3f3f3f;
int dp[2][N];
int a[N], b[N];
int main(){
int t;
scanf("%d", &t);
while(t--){
memset(dp[0], 0, sizeof(dp));
int n, m;
scanf("%d", &n);
for(int i = 1; i <= n; i++){
scanf("%d", &a[i]);
}
scanf("%d", &m);
for(int i = 1; i <= m; i++){
scanf("%d", &b[i]);
}
int ans = 0;
for(int i = 1; i <= n; i++){
int max_dp = 0;
for(int j = 1; j <= m; j++){
dp[i&1][j] = dp[(i - 1)&1][j];
//此时a[i]是可能会存在b[j] == a[i]时的a[i],所以把a[i]看作b[j],b[j]看作b[k],
//这样就和O(n^3)的解法中的状态转移方程相一致
if(a[i] > b[j]){
max_dp = max(max_dp, dp[(i - 1)&1][j]);
}else if(a[i] == b[j]){
dp[i&1][j] = max_dp + 1;
}
ans = max(ans, dp[i&1][j]);
}
}
printf("%d\n", ans);
}
}
Bone Collector - HDU 2602
题目大意
(虽然是英文但是依然略!)
思路
01背包模板题,因为之前写过了,所以这里只是回顾一下
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 1e3 + 15;
const int inf = 0x3f3f3f3f;
int w[N], c[N];
int dp[N];
int main(){
int t;
scanf("%d", &t);
while(t--){
memset(dp, 0, sizeof(dp));
int n, lim;
scanf("%d%d", &n, &lim);
for(int i = 1; i <= n; i++) scanf("%d", &c[i]);
for(int i = 1; i <= n; i++) scanf("%d", &w[i]);
for(int i = 1; i <= n; i++){
for(int j = lim; j - w[i] >= 0; j--){
dp[j] = max(dp[j - w[i]] + c[i], dp[j]);
}
}
printf("%d\n", dp[lim]);
}
}