HDU 5945 浅谈单调队列优化线性动态规划方程

这里写图片描述
世界真的很大
教室游历回到机房下午的第一道题,还是比较水
一开始先想的x*t 的做法,还是比较好想
然后想了一下发现可以单调队列优化一下,然后写了一波,调的很快,然后交一发,T了
突然想到昨天学了对拍,然后就偷了一份题解对拍

对了10分钟,RE了?
一看,提莫的是个数组开小了
哎。。。。
好歹学会了对拍怎么写

看题先:

description:

年轻的理论计算机科学家Fxx为他的学生设计了一个游戏。
在每个游戏中,您将获得三个整数X,k,tX,k,t。在每个步骤中,您只能执行以下操作之一:
1.X= X-I(0<= I<= T)2.X= X-I(0<= I<= T)。
现在Fxx想让你告诉他最小的步骤,使XX成为1。

input:

In the first line, there is an integer T(1≤T≤20)T(1≤T≤20) indicating the number of test cases.
As for the following TT lines, each line contains three integers X,k,t(0≤t≤106,1≤X,k≤106)X,k,t(0≤t≤106,1≤X,k≤106)
For each text case,we assure that it’s possible to make XX become 1。

output:

For each test case, output the answer

(谷歌翻译果然厉害233)
这道题的话,第一感觉是转换思路,几次变为1变成1变几次变到x
然后就可以想到递推,转移方式有两种,所以DP的方程很快就想出来了
n^2的代码:

#include<stdio.h>
#include<cstring>
#include<algorithm>
using namespace std;

const int INF=0x3f3f3f3f;

int n,k,t,T,f[100010];

int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d%d",&n,&k,&t);
        memset(f,INF,sizeof(f));
        f[1]=0;
        for(int i=2;i<=n;i++)
        {
            for(int j=i-1;j>=i-t&&j>=1;j--)
                f[i]=min(f[i],f[j]+1);
            if(i%k==0)
                f[i]=min(f[i],f[i/k]+1);
        }
        printf("%d\n",f[n]);
    }
    return 0;
}
/*
2
9 2 1
*/

这样肯定是过不了的,然后考虑怎么优化
想了一下发现,第一种操作的转移是固定的,就是说如果i%k==0那么只能从f[i/k]+1转移得到,不需要考虑

时间复杂度主要就计算在第二种转移里面。而我们需要知道的,又是之前一段区间的答案的最小值,这个感觉可以用一个数据结构什么的维护一下,转念一想只需要最值,而且是顺序更新的话,感觉用不着什么厉害的数据结构,好像单调队列就可以

然后就想,单调队列的条件就是,首先队列的值保持单调,维护答案一直在队首,然后队列里面的时间表现单调性,还有就是弹掉的东西一定没有新入队的优。

然后对于这道题而言,单调队列弹队尾时,弹掉的都是值没有现在的小的,而且现在更新的fi肯定在时间上更近。如果被弹掉的点可以更新,那么这个新的点肯定可以更新,那么就可以用单调队列了

单调队列可比线段树什么的优多了

完整代码:

#include<stdio.h>
#include<cstring>
#include<algorithm>
using namespace std;

const int INF=0x3f3f3f3f;

int T,n,k,tt,f[1000010],state[1000010];

int main()
{
    scanf("%d",&T);
    while(T--)
    {
        int h=1,t=1;
        scanf("%d%d%d",&n,&k,&tt);
        memset(f,INF,sizeof(f));
        f[1]=0;state[h]=1;
        for(int i=2;i<=n;i++)
        {
            while(i-state[h]>tt && h<=t) h++;
            f[i]=f[state[h]]+1;
            if(i%k==0) f[i]=min(f[i],f[i/k]+1);
            while(f[state[t]]>f[i] && h<=t) t--;
            state[++t]=i;
        }
        printf("%d\n",f[n]);
    }
    return 0;
}
/*
EL PSY CONGROO
*/

嗯,就是这样

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值