Little Bird

【题目描述】

有N(2 <= N <= 10^6)棵树,第i棵树的高度是D[i](1 <= D[i] <= 10^9)。雏鸟要从第1棵树到第N棵树去找它的朋友玩。如果雏鸟在第i棵树,那么它可以跳到第i+1、i+2、······、i+K(1 <= K <= N-1)棵树。如果雏鸟跳到一棵不矮于当前树的树,那么他的劳累值会+1,否则不会。而且雏鸟要最小化劳累值。

【输入描述】

第一行1个整数N表示树的个数;

第二行N个整数分别表示第i棵树的高度D[i];

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

接下来Q行,每行1个整数表示K。

【输出描述】

输出Q行,每行1个整数表示雏鸟在此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\n",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\n",f[n]);
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值