Little Bird

【题目描述】

现有N(2 <= N <= 106)棵树,第i棵树的高度为Di(1 <= Di <= 109)。

雏鸟想要从第1棵树跳到第N棵树,如果雏鸟在第i棵树,那么它可以跳到第i+1、i+2、······、i+K(1 <= K < N)棵树。如果雏鸟跳到一棵不矮于当前树的树,那么它的劳累值会+1,否则不会。

询问雏鸟所能达成的最小劳累值是多少。

【输入描述】

第一行输入一个整数N,表示树的个数;

第二行输入N个整数,分别表示第i棵树的高度Di

第三行输入一个整数Q(1 <= Q <= 25),表示K的个数;

接下来Q行,每行输入一个整数K。

【输出描述】

输出Q行,每行包含一个整数,表示雏鸟在此K情况下所能达到的最小劳累值。

【输入样例】

9

4 6 3 6 3 7 2 6 5

2

2

5

【输出样例】

2

1

普通的O(nmk)DP解法:

源代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int m,n,i[1000001],f[1000001];
void Solve(int k)
{
    f[1]=0;
    for (int a=1;a<=n;a++)
      for (int b=a-1;b>=a-k&&b>0;b--)
        if (i[a]<i[b])
          f[a]=min(f[a],f[b]);
        else
          f[a]=min(f[a],f[b]+1);
    printf("%d
",f[n]);
}
int main()
{
    scanf("%d",&n);
    for (int a=1;a<=n;a++)
      scanf("%d",&i[a]);
    scanf("%d",&m);
    for (int a=1;a<=m;a++)
    {
        int k;
        scanf("%d",&k);
        memset(f,0x3f,sizeof(f));
        Solve(k);
    }
    return 0;
}

单调队列优化的O(nm)解法:

源代码:

#include<cstdio>
#include<algorithm>
using namespace std;
int m,n,k,i[1000001],f[1000001],Q[1000001],head,tail;
int main() //竟然可以优化到O(n),看来优先队列挺值得学习的。
{
    scanf("%d",&n);
    for (int a=1;a<=n;a++)
      scanf("%d",&i[a]);
    scanf("%d",&m);
    while (m--)
    {
        head=1; //队头。
        tail=0; //队尾。
        scanf("%d",&k);
        f[1]=0; //开头并不需要花费体力。
        Q[++tail]=1; //Q[]中存储的是树的编号。
        for(int a=2;a<=n;a++) //DP过程。
        {
            if (a-Q[head]>k) //距离>K,故已过期。
              head++;
            if (i[a]>=i[Q[head]]) //状态转移方程。
              f[a]=f[Q[head]]+1;
            else
              f[a]=f[Q[head]];
            while (head<=tail&&f[a]<=f[Q[tail]]) //跳到tail树的代价>a树的代价时。
            {
                if (f[a]==f[Q[tail]]&&i[a]<i[Q[tail]]) //代价相等取其高。
                  break;
                tail--; //跳出没有用的节点。
            }
            Q[++tail]=a; //处理完后就可以在合适的地方入队了。
        }
        printf("%d
",f[n]);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/Ackermann/p/5703724.html