kuangbin题单|基础dp
1.HDU1024 Max Sum Plus Plus 最大M子段和
表示以第j个数结尾且被分为i段的最大子段和,答案即是。
与单集合()的最大字段和不同的点在于,本题需要组成个不相交的最大子段,因而对状态转移方程进行修改:
-
第一种情况:第个数纳入前个数的最右最大子段中,即
-
第二种情况:第个数不被纳入最右侧的最大子段,作为第i个子段存在,即。
综上:
注意到范围虽然很小不会卡成,但依然会,故考虑滚动数组优化:
其中表示前j个的最大值,通过滚动数组维护并更新。
复杂度
#include<cstdio>
#include<iostream>
#include<cstring>
const int maxn = 1e6 + 10;
const int inf = 0x3f3f3f3f;
int n, m, a[maxn];
int f[maxn], last[maxn];
int tmp = -inf;
void dp() {
for (int i = 1; i <= m; i++) {
tmp = -inf;
for (int j = i; j <= n; j++) {//j<i是无法分出i组子段的
f[j] = std::max(f[j - 1] + a[j], last[j - 1] + a[j]);
last[j - 1] = tmp;
tmp = std::max(tmp, f[j]);
}
}
}
int main() {
while (scanf("%d%d", &m, &n) == 2) {
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]);
memset(f, 0, sizeof(f));
memset(last, 0, sizeof(last));
dp();
printf("%d\n", tmp);
}
}
2.HDU1029 Ignatius and the Princess IV 计数?
存一下出现次数就行,用不着
#include<iostream>
#include<cstdio>
#include<map>
std::map<int, int> m;
int n, x, ans;
int main() {
while (scanf("%d", &n) == 1) {
m.clear();
for (int i = 1; i <= n; i++) {
scanf("%d", &x);
m[x]++;
if (m[x] >= (n + 1) / 2) {
ans = x;
}
}
printf("%d\n", ans);
}
return 0;
}
3.HDU1069 Monkey and Banana 三维偏序LIS+贪心
实质是三维的最长上升子序列问题。对于每一个三维坐标,可以衍生种堆叠方式,即:
x | y | z |
---|---|---|
a | b | c |
a | b | c |
b | a | c |
b | c | a |
c | a | b |
c | b | a |
考虑贪心策略,升序排列与。剩下的就是求的模板:令表示以第i个数结尾的最长上升子序列,转移方程:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<map>
#include<algorithm>
const int maxn = 30 + 10;
struct node {
int siz[3];
} t[maxn];
struct node2 {
int x, y, z;
bool operator<(const node2 &p) const {
return x == p.x ? y < p.y : x < p.x;
}
} t2[maxn * 6];
int dp[maxn * 6];
int n, tot, T;
int f() {
int ans = 0;
for (int i = 1; i <= 6 * n; i++) {
dp[i] = t2[i].z;
for (int j = 1; j < i; j++) {
if (t2[j].y < t2[i].y && t2[j].x < t2[i].x)
dp[i] = std::max(dp[i], dp[j] + t2[i].z);
}
ans = std::max(ans, dp[i]);
}
return ans;
}
int main() {
while (scanf("%d", &n) == 1 && n) {
tot = 0;
memset(t, 0, sizeof(t));
memset(t2, 0, sizeof(t2));
memset(dp, 0, sizeof(dp));
for (int i = 1; i <= n; i++)
scanf("%d%d%d", &t[i].siz[0], &t[i].siz[1], &t[i].siz[2]);
for (int i = 1; i <= n; i++) {
t2[++tot] = (node2) {t[i].siz[0], t[i].siz[1], t[i].siz[2]};
t2[++tot] = (node2) {t[i].siz[0], t[i].siz[2], t[i].siz[1]};
t2[++tot] = (node2) {t[i].siz[1], t[i].siz[0], t[i].siz[2]};
t2[++tot] = (node2) {t[i].siz[1], t[i].siz[2], t[i].siz[0]};
t2[++tot] = (node2) {t[i].siz[2], t[i].siz[1], t[i].siz[0]};
t2[++tot] = (node2) {t[i].siz[2], t[i].siz[0], t[i].siz[1]};
}
std::sort(t2, t2 + n * 6);
int res = f();
printf("Case %d: maximum height = %d\n", ++T, res);
}
return 0;
}
4.HDU1074 Doing Homework 状压dp
令表示作业状态为时的最小超时。对于状态,如果中第项作业是完成的,那么可以考虑转移状态:
其中是完成该项作业的超时时间,如果不超时则为。
因为题目要求字典序最小,而我们的转移过程是逆序的,故必须逆序枚举以满足字典序要求。
#include<iostream>
#include<cstdio>
#include<cstring>
const int maxn = 15 + 5;
const int inf = 0x3f3f3f3f;
int dp[1 << maxn], cost[1 << maxn];
int head[1 << maxn];
int T, n;
struct node {
int c, d;
std::string s;
} t[maxn];
void out(int x) {
if (!x) return;
out(x - (1 << head[x]));
std::cout << t[head[x]].s << std::endl;
}
int main() {
scanf("%d", &T);
while (T--) {
memset(head, 0, sizeof(head));
memset(cost, 0, sizeof(cost));
scanf("%d", &n);
for (int i = 0; i < n; i++)
std::cin >> t[i].s >> t[i].d >> t[i].c;
for (int i = 1; i < (1 << n); i++) {
dp[i] = inf;
for (int j = n - 1; j >= 0; j--) {
if (!(i & (1 << j))) continue;
int cos = std::max(0, cost[i - (1 << j)] + t[j].c - t[j].d);
if (dp[i] > dp[i - (1 << j)] + cos) {
dp[i] = dp[i - (1 << j)] + cos;
cost[i] = cost[i - (1 << j)] + t[j].c;
head[i] = j;
}
}
}
printf("%d\n", dp[(1 << n) - 1]);
out((1 << n) - 1);
}
return 0;
}
5.HDU1087 Super Jumping! Jumping! Jumping! 最大上升子序列和
令表示以结尾的最大上升子序列和
#include<iostream>
#include<cstdio>
#include<cstring>
const int maxn = 1000 + 10;
int n, a[maxn];
int dp[maxn], res;
int main() {
while (scanf("%d", &n) && n) {
res = 0;
memset(dp, 0, sizeof(dp));
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]);
for (int i = 1; i <= n; i++) {
dp[i] = a[i];
for (int j = 1; j < i; j++) {
if (a[j] < a[i])
dp[i] = std::max(dp[i], dp[j] + a[i]);
}
res = std::max(res, dp[i]);
}
printf("%d\n", res);
}
return 0;
}
6.HDU1114 Piggy-Bank 完全背包
最小完全背包。令表示重量为i时的最小钱币储存量,跑模板就是了。
#include<iostream>
#include<cstdio>
#include<cstring>
const int maxn = 1e4 + 10;
const int inf = 0x3f3f3f3f;
int T;
int E, F;
int n;
int w[maxn], c[maxn];
int dp[maxn];
int main() {
scanf("%d", &T);
while (T--) {
scanf("%d%d", &E, &F);
scanf("%d", &n);
dp[0] = 0;
for (int i = 1; i <= F - E; i++)
dp[i] = inf;
for (int i = 1; i <= n; i++)
scanf("%d%d", &w[i], &c[i]);
for (int i = 1; i <= n; i++)
for (int j = c[i]; j <= F - E; j++)
dp[j] = std::min(dp[j], dp[j - c[i]] + w[i]);
if (dp[F - E] == inf) puts("This is impossible.");
else printf("The minimum amount of money in the piggy-bank is %d.\n",]
7.HDU1176 免费馅饼 简单dp
表示第秒位于第个位置上的最大价值,第个位置可以由三个位置转移过来。
参考了一些方法防止下标越界,原本写的很抽象。
#include<iostream>
#include<cstdio>
#include<cstring>
const int maxn = 100000+10;
const int inf = 0x3f3f3f3f;
int n, x, T;
int dp[maxn][10 + 5];
int sum[maxn][10 + 5];
int maxT, res;
int main() {
while (scanf("%d", &n) == 1 && n) {
maxT = res = -inf;
memset(sum, 0, sizeof(sum));
memset(dp, -inf, sizeof(dp));
for (int i = 1; i <= n; i++) {
scanf("%d%d", &x, &T);
sum[T][x + 1]++;
maxT = std::max(maxT, T);
}
dp[0][5] = dp[0][6] = dp[0][7] = 0;
for (int i = 1; i <= maxT; i++)
for (int j = 1; j <= 11; j++)
dp[i][j] = std::max(dp[i - 1][j] + sum[i][j],
std::max(dp[i - 1][j - 1] + sum[i][j - 1], dp[i - 1][j + 1] + sum[i][j + 1]));
for (int i = 1; i <= 11; i++)
res = std::max(res, dp[maxT][i]);
printf("%d\n", res);
}
return 0;
}
8.HDU1260 Tickets 简单dp
原本以为是区间dp,写一半发现区间只能从左到右转移,不是任意转移。
令表示前i个数的最小代价,可以发现对于第个人要么单独买票,要么和第个人一起买票。
#include<iostream>
#include<cstdio>
#include<cstring>
const int maxn = 2000 + 10;
const int inf = 0x3f3f3f3f;
int n;
int k, a[maxn], cost[maxn];
int dp[maxn];
int main() {
scanf("%d", &n);
while (n--) {
memset(dp, inf, sizeof(dp));
scanf("%d", &k);
for (int i = 1; i <= k; i++) {
scanf("%d", &a[i]);
}
for (int i = 1; i <= k - 1; i++) {
scanf("%d", &cost[i]);
}
dp[0] = 0;
dp[1] = a[1];
for (int i = 2; i <= k; i++)
dp[i] = std::min(dp[i - 1] + a[i], dp[i - 2] + cost[i - 1]);
//printf("%d\n", dp[k]);
int HH = (dp[k] / 3600 + 8) % 24, time = dp[k] % 3600;
int MM = time / 60, SS = time % 60;
if (HH >= 12) {
printf("%02d:%02d:%02d pm\n", HH - 12, MM, SS);
} else printf("%02d:%02d:%02d am\n", HH, MM, SS);
}
return 0;
}
9.HDU1257 最少拦截系统 最长上升子序列
由引理可知:最少的不上升子序列的个数就是最长上升子序列的长度。
然后就是求模板了。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
const int maxn = 1e5 + 7;
int n, a[maxn];
int dp[maxn];
int main() {
while (~scanf("%d", &n)) {
memset(dp, 0, sizeof(dp));
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]);
dp[1] = a[1];
int len = 1;
for (int i = 2; i <= n; i++) {
if (a[i] > dp[len]) dp[++len] = a[i];
else *std::upper_bound(dp + 1, dp + 1 + len, a[i]) = a[i];
}
printf("%d\n", len);
}
return 0;
}
10.HDU1160 FatMouse's Speed 二维偏序LIS
对其中一维进行排序,对另一维求即可。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
const int maxn = 1000 + 10;
int n, res, id;
struct node {
int w, v;
bool operator<(const node &p) const {
return w == p.w ? v > p.v : w < p.w;
}
} t[maxn];
int head[maxn], dp[maxn];
void out(int x) {
if (!x || !head[x]) return;
printf("%d\n", head[x]);
out(head[x]);
}
int main() {
while (scanf("%d%d", &t[++n].w, &t[n].v) != EOF);
std::sort(t + 1, t + 1 + n);
for (int i = 1; i <= n; i++) {
for (int j = 1; j < i; j++) {
if (t[j].w < t[i].w && t[j].v > t[i].v) {
if (dp[i] < dp[j] + 1) {
dp[i] = dp[j] + 1;
head[i] = j;
}
}
}
if (dp[i] > res) {
res = dp[i];
id = i;
}
}
printf("%d\n", res);
out(id);
}
11.POJ1015/UVA323 Jury Compromise 01背包+拔河问题
拔河问题见洛谷P1282。本题难度在路径输出上。
类似拔河问题,设为前个人选择了个人的方案(类型)
那么显然,类似背包可得出转移方程如下:
不断更新,如果相等则更新
时间复杂度
考虑优化(多米诺问题的分解方法),求最小下的最大->求每一个下的最大:
令表示时的最大和。枚举每一个下的最大和,那么转移方程变为:
时间复杂度
路径输出:
如果仅用二维数组记录每一个合法的,那么显然会被覆盖。故增加一维,即。
#include<iostream>
#include<cstdio>
#include<cstring>
const int maxn = 200 + 3;
const int inf = 0x3f3f3f3f;
int dp[20 + 5][20 * 20 * 2];//前i个人选择j个人,差为k的最大d+p和
int head[maxn][20 + 5][20 * 20 * 2];
int d[maxn], p[maxn];
int n, m, T;
int ans1, ans2;
int id[maxn], tot;
int base = 20 * 20;
void out(int i, int j, int k) {
if (!j) return;
int D = d[head[i][j][k + base]], P = p[head[i][j][k + base]];
out(head[i][j][k + base] - 1, j - 1, k - (D - P));
ans1 += D, ans2 += P;
id[++tot] = head[i][j][k + base];
}
int main() {
while (scanf("%d%d", &n, &m) == 2 && n && m) {
memset(dp, -inf, sizeof(dp));
memset(id, 0, sizeof(id));
memset(head, 0, sizeof(head));
tot = ans1 = ans2 = 0;
for (int i = 1; i <= n; i++)
scanf("%d%d", &d[i], &p[i]);
//base = 20 * 20;
dp[0][0 + base] = 0;
for (int i = 1; i <= n; i++) {
for (int j = 0; j <= m; j++)
for (int k = -20 * 20; k <= 20 * 20; k++)
head[i][j][k + base] = head[i - 1][j][k + base];
for (int j = m; j; j--)
for (int k = -20 * 20; k <= 20 * 20; k++) {
int temp = k - (d[i] - p[i]);
if (temp < -20 * 20 || temp > 20 * 20) continue;
if (dp[j][k + base] < dp[j - 1][temp + base] + d[i] + p[i]) {
dp[j][k + base] = dp[j - 1][temp + base] + d[i] + p[i];
head[i][j][k + base] = i;
}
}
}
int res = 20 * 20;
for (int i = -20 * 20; i <= 20 * 20; i++) {
int temp = abs(i);
if (dp[m][i + base] < 0) continue;
if (temp < abs(res)) {//寻找最小差值k
res = i;
} else if (temp == abs(res)) {
if (dp[m][i + base] > dp[m][res + base]) res = i;
}
}
out(n, m, res);
printf("Jury #%d\n", ++T);
printf("Best jury has value %d for prosecution and value %d for defence:\n", ans1, ans2);
for (int i = 1; i <= tot; i++) {
printf(" %d", id[i]);
}
printf("\n\n");
}
return 0;
}
12.POJ1458 Common Subsequence 最长公共子序列
令表示两个序列对应位置分别为和的最长公共子序列。
如果,那么;否则取的最大值。
#include<iostream>
#include<cstdio>
#include<cstring>
const int maxn = 1000 + 10;
std::string a, b;
int dp[maxn][maxn];
int T;
int main() {
while (std::cin >> a >> b) {
int res = 0, ans = 0;
memset(dp, 0, sizeof(dp));
int len1 = a.length(), len2 = b.length();
for (int i = 1; i <= len1; i++) {
for (int j = 1; j <= len2; j++) {
int x = a[i - 1], y = b[j - 1];
if (x == y) dp[i][j] = dp[i - 1][j - 1] + 1;
else dp[i][j] = std::max(dp[i - 1][j], dp[i][j - 1]);
}
}
printf("%d\n", dp[len1][len2]);
}
return 0;
}
13.POJ1661 Help Jimmy 构造dp
好题目,参考此处
原问题涉及的变量很多,但我们发现,的初始位置可以视为一个长度为,高度为的木板。同样,地面也可以视为一个长度为,高度为的木板。故而的过程实质是在两两木板上转移,所以不妨设为在第个木板上,以左端点/右端点为起点的最短时间。
将n+1个木板按高度排序。对于第个木板,寻找下一个高度差不超过且垂直掉落的木板。考虑i->k的转移:从第块木板左端点/右端点移动到第块木板左端点/右端点累计的时间即为垂直掉落的距离+横向移动的距离,这个是容易计算的。
以上的寻找过程只能遍历到第块,第块与第块(地面)的转移需要特判。答案即为
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
const int maxn = 20000 + 10;
const int inf = 0x3f3f3f3f;
int n, max;
struct node {
int x1, x2;
int h;
bool operator<(const node &p) const {
return h < p.h;
}
} t[maxn];
int dp[maxn][2];
int T;
int x, y;
void left(int i) {
int k = i - 1;
while (k > 0 && (t[i].h - t[k].h) <= max) {
if (t[i].x1 >= t[k].x1 && t[i].x1 <= t[k].x2) {
dp[i][0] = t[i].h - t[k].h + std::min(dp[k][0] + t[i].x1 - t[k].x1, dp[k][1] + t[k].x2 - t[i].x1);
return;//掉到下一块板上并向左走/向右走
} else k--;//当前板不在脚下
}//处理1~i-1号木板
if (t[i].h - t[k].h <= max) dp[i][0] = t[i].h - 0;//处理1号跳到0号木板(0号是地面)
else dp[i][0] = inf;
}
void right(int i) {
int k = i - 1;
while (k > 0 && (t[i].h - t[k].h) <= max) {
if (t[i].x2 >= t[k].x1 && t[i].x2 <= t[k].x2) {
dp[i][1] = t[i].h - t[k].h + std::min(dp[k][0] + t[i].x2 - t[k].x1, dp[k][1] + t[k].x2 - t[i].x2);
return;//掉到下一块板上并向左走/向右走
} else k--;//当前板不在脚下
}//处理2~i-1号木板
if (t[i].h - t[k].h <= max) dp[i][1] = t[i].h - 0;
else dp[i][1] = inf;
}
int main() {
scanf("%d", &T);
while (T--) {
scanf("%d%d%d%d", &n, &x, &y, &max);
for (int i = 1; i <= n; i++)
scanf("%d%d%d", &t[i].x1, &t[i].x2, &t[i].h);
t[n + 1] = (node) {x, x, y};
t[0] = (node) {-20000, 20000, 0};
std::sort(t, t + n + 1);
memset(dp, 0, sizeof(dp));
for (int i = 1; i <= n + 1; i++)
left(i), right(i);
printf("%d\n", std::min(dp[n + 1][0], dp[n + 1][1]));
}
}
14.POJ2533 Longest Ordered Subsequence LIS
再裸不过的LIS了。
#include<iostream>
#include<cstdio>
#include<algorithm>
const int maxn = 1000 + 10;
int dp[maxn];
int a[maxn], len = 1, n;
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]);
dp[1] = a[1];
for (int i = 2; i <= n; i++) {
if (a[i] > dp[len]) dp[++len] = a[i];
else *std::upper_bound(dp + 1, dp + 1 + len, a[i]) = a[i];
}
printf("%d", len);
return 0;
}
15.POJ3186/USACO06FEB Treats for the Cows 区间dp
设为段的价值,可以发现,对于一个长度为的子段,其寿命为。那么转移方程如下:
初始化
#include<iostream>
#include<cstdio>
const int maxn = 2000 + 10;
int n, v[maxn];
int dp[maxn][maxn];
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++)
scanf("%d", &v[i]);
for (int i = 1; i <= n; i++)
dp[i][i] = v[i] * n;
for (int len = 2; len <= n; len++)
for (int i = 1; i + len - 1 <= n; i++) {
int j = i + len - 1;
dp[i][j] = std::max(dp[i + 1][j] + (n - len + 1) * v[i], dp[i][j - 1] + (n - len + 1) * v[j]);
}
printf("%d", dp[1][n]);
return 0;
}
16.HDU1078 FatMouse and Cheese 记忆化搜索
一开始写了个然后了。
一个巧妙的思路:枚举每一个,在每一个下跑记忆化搜索,即,是从该点出发走步()的最大累计值。
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
const int maxn = 100 + 10;
const int dx[] = {-1, 0, 0, 1};
const int dy[] = {0, -1, 1, 0};
int n, k;
int cheese[maxn][maxn], ans;
int dp[maxn][maxn];
bool vis[maxn][maxn];
int dfs(int x, int y) {
if (dp[x][y]) return dp[x][y];
int res = 0;
for (int i = 1; i <= k; i++)
for (int j = 0; j < 4; j++) {
int nx = x + dx[j] * i, ny = y + dy[j] * i;
if (nx < 0 || ny < 0 || nx >= n || ny >= n) continue;
if (cheese[nx][ny] > cheese[x][y]) {
res = std::max(res, dfs(nx, ny));
}
}
return dp[x][y] = res + cheese[x][y];
}
int main() {
while (scanf("%d%d", &n, &k) == 2 && n != -1 && k != -1) {
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
scanf("%d", &cheese[i][j]);
memset(dp, 0, sizeof(dp));
dfs(0, 0);
printf("%d\n", dp[0][0]);
}
return 0;
}
17.HDU2859 Phalanx 最大对称子矩阵
令表示以为左下角的最大对称子矩阵。初始化
考虑的转移:设为以为左下角,向上/右分别展开的匹配数。
则。为什么是取最小?因为对称子矩阵的要求使得已经被限制住了,只能衍生最外侧。如
ctx
cpb
zcc
因为的存在,故而即使,但也只能在
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn = 1000 + 10;
char ch[maxn][maxn];
int dp[maxn][maxn];
int n;
int main() {
while (scanf("%d", &n) == 1 && n) {
int ans = 1;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
cin >> ch[i][j];
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++) {
if (i == 1 || j == n) {
dp[i][j] = 1;
continue;
}
int x = i, y = j;
while (x >= 1 && y <= n && ch[x][j] == ch[i][y]) {
x--, y++;
}
int temp = i - x;
dp[i][j] = temp > dp[i - 1][j + 1] + 1 ? dp[i - 1][j + 1] + 1 : temp;
ans = std::max(ans, dp[i][j]);
}
printf("%d\n", ans);
}
return 0;
}
18.POJ3616/LuoguP2889 Milking Time S 区间覆盖
左端点排序,令表示选择第个区间的最大累计值
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
const int maxn = 1e3 + 10;
struct node {
int l, r, w;
bool operator<(const node &p) const {
return l < p.l;
}
} t[maxn];
int n, m, r;
int dp[maxn], res;
int main() {
scanf("%d%d%d", &n, &m, &r);
for (int i = 1; i <= m; i++)
scanf("%d%d%d", &t[i].l, &t[i].r, &t[i].w);
std::sort(t + 1, t + 1 + m);
for (int i = 1; i <= m; i++) {
dp[i] = t[i].w;
for (int j = 1; j < i; j++)
if (t[i].l - t[j].r >= r)
dp[i] = std::max(dp[i], dp[j] + t[i].w);
}
for (int i = 1; i <= m; i++)
res = std::max(res, dp[i]);
printf("%d", res);
return 0;
}
19.[USACO08FEB] Making the GradeG 结论题/dp
本题是个广为人知的结论题,即存在一个做法:维护一个大根堆,如果加入元素堆顶则加入,否则将堆顶元素变为加入元素并计入代价。详细见@此处
然而本题范围并不严格,以下是做法:
令表示已经完成前个数,的最小代价。其中,数组是排序并离散化后的数组。转移方程为:
其中的可以通过滚动数组来维护,不需要枚举。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn = 2000 + 10;
int n, a[maxn];
int b[maxn], m;
int tmp, res = 0x3f3f3f3f;
int dp[maxn][maxn];//前i位,a[i]=b[j]的最小花费
void solve() {
memset(dp, 0x3f, sizeof(dp));
dp[0][0] = 0;
for (int i = 1; i <= n; i++) {
tmp = dp[i - 1][0];
for (int j = 1; j <= m; j++) {
tmp = std::min(tmp, dp[i - 1][j]);//tmp为dp[i-1][k]的最小值(k \in [0,j])
dp[i][j] = tmp + abs(a[i] - b[j]);
}
}
for (int i = 1; i <= m; i++)
res = std::min(res, dp[n][i]);
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]), b[i] = a[i];
sort(b + 1, b + 1 + n);
m = unique(b + 1, b + 1 + n) - b - 1;
solve();
reverse(a + 1, a + 1 + n);
solve();
printf("%d", res);
return 0;
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 实操Deepseek接入个人知识库
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· 易语言 —— 开山篇