青蛙过桥 DP + 字典序
题目
一座长度为n的桥,起点的一端坐标为0,且在整数坐标i处有a[i]个石头【0<=a[i]<=4】,
一只青蛙从坐标0处开始起跳,一步可以跳的距离为1或2或3【即每一步都会落在整数点处】,
青蛙落在i处会踩着该点的所有石头,求青蛙跳出这座桥最少踩多少个石头?并且输出依次跳
过的坐标点路线,如果存在多种路线,输出字典序最小的那一条。输入格式:
第一行整数n(<150000),接着下一行会有n+1个由空格隔开的整数,即桥上各个坐标处石头数量。
输出格式:
第一行为踩着最少石头个数,第二行为依次跳过的坐标点【字典序最小的】。
输入样例:
在这里给出两组输入。例如:
10 1 2 1 3 0 3 1 2 1 1 2 100 1 2 0 4 0 1 3 4 2 2 1 3 1 4 0 3 0 1 2 3 3 2 2 0 1 0 0 0 0 1 2 1 3 4 0 3 4 4 1 0 4 1 3 1 1 2 3 4 4 4 0 2 0 1 1 1 3 1 3 2 1 2 4 1 2 1 4 1 0 0 1 2 3 0 2 4 4 0 0 4 2 0 0 2 1 3 3 3 0 0 2 0 0 1 2 4 2 2 2 4 0输出样例:
在这里给出对应的输出。例如:
4 0 2 4 6 8 36 0 2 4 5 8 10 12 14 16 17 20 23 25 26 27 28 31 34 35 38 39 41 44 47 50 52 54 57 60 63 65 68 69 70 73 74 77 78 81 82 85 88 89 91 92 94 97 100Code Size Limit
16 KB
Time Limit
1000 ms
Memory Limit
64 MB
思路
一开始求的时候正序, 但是其实这样并不能保证字典序, 我们可以采取倒序的方法, 倒序 我们只需要保证优先走最大距离的步数,这样能够同时保证字典位数最小,并且字典序最小(数字最小), 所以直接reverse, 然后dp就行了, 这里有个小技巧,其实输出不用保存在reverse,直接递归先中控制一下即可。
Code
#include <bits/stdc++.h>
// #inclue
using i64 = long long;
const int N = 5e5;
int n, a[N], dp[N], cnt;
int pre[N];
std::vector<int> g[N];
void out(int s) {
if (s == 0) {
// std::cout << s;
// g[cnt].push_back(s);
return;
}
if (n - s + 1) {
std::cout << " ";
}
std::cout << n - s + 1;
out(pre[s]);
}
int main() {
std::cin >> n;
for (int i = 0; i <= n; i ++) {
std::cin >> a[i];
}
std::reverse(a + 0, a + 2 + n);
// for (int i = 0; i <= n + 1; i ++) {
// std::cout << a[i] << " \n"[i == n + 1];
// }
dp[0] = a[0];
for (int i = 1; i <= n + 1; i ++) {
dp[i] = dp[i - 1] + a[i];
pre[i] = i - 1;
if (i - 2 >= 0) {
if (dp[i] > dp[i - 2] + a[i]) {
pre[i] = i - 2;
}
dp[i] = std::min(dp[i], dp[i - 2] + a[i]);
}
if (i - 3 >= 0) {
if (dp[i] > dp[i - 3] + a[i]) {
pre[i] = i - 3;
}
dp[i] = std::min(dp[i], dp[i - 3] + a[i]);
}
}
std::cout << dp[n + 1] << "\n";
out(n + 1);
}
浙公网安备 33010602011771号