hdu 5945 Fxx and game(单调队列OR标记过程的bfs)@



Fxx and game

Time Limit: 3000/1500 MS (Java/Others)    Memory Limit: 131072/65536 K (Java/Others)
Total Submission(s): 486    Accepted Submission(s): 120


Problem Description
Young theoretical computer scientist Fxx designed a game for his students.

In each game, you will get three integers  X,k,t .In each step, you can only do one of the following moves:

1.X=Xi(0<=i<=t) .

2. if  k|X,X=X/k .

Now Fxx wants you to tell him the minimum steps to make  X  become 1.
 

Input
In the first line, there is an integer  T(1T20)  indicating the number of test cases.

As for the following  T  lines, each line contains three integers  X,k,t(0t106,1X,k106)

For each text case,we assure that it's possible to make  X  become 1。
 

Output
For each test case, output the answer.
 

Sample Input
  
  
2 9 2 1 11 3 3
 

Sample Output
  
  
4 3
 


给定x, k,  t,求将x变成1的最小步数,每次可以减去(1到t的值),或者如果当前值余k等于0可以除以k。

解:因为t的范围很大,所以用搜索容易超时,这里借助单调队列。



#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int N = 1000005;
const int inf = 0x3f;
int q[N], f[N];


int main()
{
    int T, x, k, t;
    scanf("%d", &T);
    while(T--)
    {
        scanf("%d %d %d", &x, &k, &t);
        memset(f,inf,sizeof(f));
        q[0]=1, f[1]=0;
        int l=0, r=1;
        for(int i=2;i<=x;i++)
        {
            if(!(i%k))
                f[i]=min(f[i],f[i/k]+1);
            while(l<r&&q[l]+t<i) l++;
            f[i]=min(f[i],f[q[l]]+1);
            while(l<r&&f[i]<f[q[r-1]]) r--;
            q[r++]=i;
        }
        printf("%d\n",f[x]);
    }
    return 0;
}



单调队列是指:队列中元素之间的关系具有单调性,而且,队首和队尾都可以进行出队操作,只有队尾可以进行入队操作。


以单调不减队列为例:队列内的元素(e1,e2,e3...en)存在(e1<=e2<=e3<=...<=en)的关系,所以队首元素e1一定是最小的元素。与优先队列不同的是,当有一个新的元素e入队时,先要将队尾的所有大于e的元素弹出,以保证单调性,再让元素e入队尾。


例如这样一组数(1,3,2,1,5,6),进入单调不减队列的过程如下:

1入队,得到队列(1);

3入队,得到队列(1,3);

2入队,这时,队尾的的元素3>2,将3从队尾弹出,新的队尾元素1<2,不用弹出,将2入队,得到队列(1,2);

1入队,2>1,将2从队尾弹出,得到队列(1,1);

5入队,得到队列(1,1,5);

6入队,得到队列(1,1,5,6);


代码实现:

[cpp]  view plain  copy
 print ?
  1. #include <iostream>  
  2.   
  3. using namespace std;  
  4.   
  5. class MonotonicQueue  
  6. {  
  7.   
  8. #define MAXN 1000000  
  9.   
  10. public:  
  11.     MonotonicQueue(void)  
  12.     {  
  13.         q = new int[MAXN+5];  
  14.         f = r = 0;  
  15.     }  
  16.     void push(const int val)  
  17.     {  
  18.         while (r > f && q[r-1] > val) //弹出大于新元素的队尾元素  
  19.         {  
  20.             r--;  
  21.         }  
  22.         q[r++] = val;  
  23.     }  
  24.     int front(void)  
  25.     {  
  26.         if (f < r)  
  27.         {  
  28.             return q[f];  
  29.         }  
  30.         return 0;  
  31.     }  
  32.     void pop_front(void)  
  33.     {  
  34.         if (f < r)  
  35.         {  
  36.             f++;  
  37.         }  
  38.     }  
  39.     bool isEmpty(void)  
  40.     {  
  41.         return f == r;  
  42.     }  
  43.     void clear(void)  
  44.     {  
  45.         f = r = 0;  
  46.     }  
  47.     ~MonotonicQueue(void)  
  48.     {  
  49.         delete q;  
  50.     }  
  51. private:  
  52.     int *q;  
  53.     int f;  
  54.     int r;  
  55. };  
  56.   
  57. int main(void)  
  58. {  
  59.     MonotonicQueue mq;  
  60.     int n;  
  61.     while (cin >> n)  
  62.     {  
  63.         int i;  
  64.         for (i = 0; i < n; i++)  
  65.         {  
  66.             int a;  
  67.             cin >> a;  
  68.             mq.push(a);  
  69.         }  
  70.   
  71.         while (!mq.isEmpty())  
  72.         {  
  73.             cout << mq.front() << endl;  
  74.             mq.pop_front();  
  75.         }  
  76.         mq.clear();  
  77.     }  
  78.     return 0;  
  79. }  
访问队首和删除队首元素的时间复杂度为O(1),在队尾加入元素的最坏情况下的时间复杂度为O(n),n为队列当前长度。所以,可以采用二分查找,来确定新加入元素应该插入的位置。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值