Codeforces Round #669 (Div. 2) D. Discrete Centrifugal Jumps(dp、单调栈)
1407 D Discrete Centrifugal Jumps
题意:给定n个楼高,要求从1位置跳到n位置。问最少的跳的次数
合法跳跃 i − > j i->j i−>j:
- i + 1 = j i+1=j i+1=j
- m a x ( a i + 1 , a i + 2 , . . . , a j − 1 ) < m i n ( a i , a j ) max(a_{i+1},a_{i+2},...,a_{j-1})<min(a_{i},a_{j}) max(ai+1,ai+2,...,aj−1)<min(ai,aj)
- m a x ( a i + 1 , a i + 2 , . . . , a j − 1 ) > m i n ( a i , a j ) max(a_{i+1},a_{i+2},...,a_{j-1})>min(a_{i},a_{j}) max(ai+1,ai+2,...,aj−1)>min(ai,aj)
思路:
首先题目要求最少跳跃次数,而且题意判断出前面的最优状态不影响之后的,想到dp
当时第一感觉是用单调栈找到当前位置 j j j处向左的第一个大于等于/小于等于他的位置然后跳跃,每次都是找最长的位置。主要代码如下:
for (int i = 1; i <= n; i++)dp[i] = maxn;
dp[0] = 0; dp[1] = 0;
for (int i = 1; i <= n; i++) {
cin >> a[i];
//向左找到第一个小于等于他的位置下标,其余位置的h都大于
while (sma.size() && a[sma.top()] > a[i]) sma.pop();
if (sma.empty()) maxx[i] = 0;
else maxx[i] = sma.top();
sma.push(i);
//向左找到第一个大于等于他的位置下标,其余位置的h都小于
while (smin.size() && a[smin.top()] < a[i])smin.pop();
if (smin.empty())minn[i] = 0;
else minn[i] = smin.top();
smin.push(i);
if (i == 1)continue;
dp[i] = dp[i - 1] + 1;
if (minn[i]) dp[i] = min(dp[i], dp[minn[i]] + 1);
if (maxx[i]) dp[i] = min(dp[i], dp[maxx[i]] + 1);
}
cout << dp[n] << endl;
但是这个思路不太对,每次都最长不一定是最优解,所以一直wa5。
其实就是在dp的时候对于每个可行的解都转移试试看,最后输出 d p n dp_{n} dpn就可,代码如下
int a[maxn], maxx[maxn], minn[maxn], dp[maxn];//记录当前位置向左,第一个大于等于/小于等于他的下标
stack<int> sma, smin;
int main()
{
int ans = maxn;
int n, tt;
//int Min = maxn, Max = 0;//记录2-当前位置的最大/最小值
cin >> n;
for (int i = 1; i <= n; i++)dp[i] = maxn;
dp[0] = 0; dp[1] = 0;
cin >> a[1];
sma.push(1);
smin.push(1);
for (int i = 2; i <= n; i++) {
cin >> a[i];
dp[i] = dp[i - 1] + 1;
//向左找第一个小于等于i的下标,中间都大于他
while (sma.size() && a[sma.top()] >= a[i]) {
tt = a[sma.top()];
sma.pop();
if (a[i] < tt&&sma.size()) dp[i] = min(dp[i], dp[sma.top()] + 1);
}
sma.push(i);
//向左找第一个大于等于i的下标,中间都小于他
while (smin.size() && a[smin.top()] <= a[i]) {
tt = a[smin.top()];
smin.pop();
if (a[i] > tt&&smin.size()) dp[i] = min(dp[i], dp[smin.top()] + 1);
}
smin.push(i);
}
cout << dp[n] << endl;
return 0;
}