HDU 5945 Fxx and game [单调队列+dp]【动态规划】

题目链接:http://acm.split.hdu.edu.cn/showproblem.php?pid=5945
————————————.

问题描述
青年理论计算机科学家Fxx给的学生设计了一款数字游戏。

一开始你将会得到一个数:XX,每次游戏将给定两个参数:k,tk,t, 任意时刻你可以对你的数执行下面两个步骤之一:

1.:X = X - i(1 <= i <= t)1.X=X−i(1<=i<=t)。

2.:2.若:X:X为:k:k的倍数,X = X / kX=X/k。

现在Fxx想要你告诉他最少的运行步骤,使:X:X变成:11。
输入描述
第一行一个整数:T(1\leq T\leq20):T(1≤T≤20)表示数据组数。

接下来:T:T行,每行三个数:X,k,t(0\leq t\leq10^6,1\leq X,k\leq10^6)X,k,t(0≤t≤10^​6​​ ,1≤X,k≤10^​6​​ )

数据保证有解。
输出描述
输出共:T:T行。

每行一个整数表示答案。
输入样例
2
9 2 1
11 3 3
输出样例
4
3

—————————————.

题目大意:中文题 不解释.

解题思路:

很容易先到是一个sbDP
状态转移方程就是
if(0==i%k)dp[i]=min(dp[i],dp[i/k]+1);
dp[i]=min(dp[i],min{dp[i+1],dp[i+2],,dp[i+t]}+1);
上述转移方程很好设计 但是因为数据很大 所以min{dp[i+1],dp[i+2],
,dp[i+t]}这个操作如果暴力求解的话复杂度是O(xt) 很不现实 ,所以要优化 。

首先想到的是挂到线段树上 每次更新一个点+查询一个区间最小值 这样的话复杂度就降到O(x*logt) 然后发现还是会超时 ,于是乎就GG了

最后结束之后发现这是一个叫做单调队列优化的东西 于是借鉴巨巨们的博客http://blog.csdn.net/guhaiteng/article/details/52972757

学习到了
单调队列 其实就是一个通过维护下标在O(1)内知道区间最小值的东西
其实有点模拟数组的思想

然后根据题意
确定一下值的范围 ,删去无用值 ,每次操作都是线性的。
这样下来 总的时间复杂度就是O(n)

好高端的东西 现在理解的还不是那么特别透 。。。。继续努力。

然后

附本题代码
——————————.

#include <bits/stdc++.h>
using namespace std;

#define INF 0x3f3f3f3f
#define pb push_back
#define abs(a) (a)>0?(a):-(a)
#define lalal puts("*******")
typedef long long int LL ;
typedef unsigned long long int LLu ;
/*******************************/

const int MOD = 1e9+7;
const int N = 1000005;
const double eps = 1e-9;


int dp[N],deq[N];//单调队列 维护下标

int main()
{
    int _;
    while(~scanf("%d",&_)){
        while(_--){

            int x,t,k;
            scanf("%d%d%d",&x,&k,&t);

            int l,r;
            l=r=1;
            deq[1]=1,dp[1]=0;

            for(int i=2;i<=x;i++){
                dp[i]=10101010;
                while(l<=r&&deq[l]<i-t)l++; //确定范围的左值 在t的范围内
                if(l<=r)dp[i]=dp[deq[l]]+1;
                if(0==i%k)dp[i]=min(dp[i],dp[i/k]+1);
                while(l<=r&&dp[deq[r]] >= dp[i]) r--;  //维护右值 使无用的数据删除掉
                deq[++r] = i; //入队
            }

            printf("%d\n",dp[x]);
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值