HDU 5945 Fxx and game 单调队列优化dp

题目链接

http://acm.hdu.edu.cn/showproblem.php?pid=5945

题目

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=X−i(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(1≤T≤20) indicating the number of test cases.

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

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,有两种操作:
1.X=X-i(0<=i<=t)
2.if k|X,X=X/k。
求最少经过多少次操作能将X变成1.

解题

设dp[i]表示将i变成1需要的最少操作次数。
那么,dp[i]=min{dp[i*k],dp[i+j]+1},0<=j<=t.
如果直接按照状态转移方程去写,时间复杂度是O(n^2).
需要用更快的方法求出min{dp[i+j]},0<=j<=t.
而求一个滑动窗口的最小值显然是单调队列的拿手好戏。
维护一个单调上升的队列即可。

这题是在别人博客里看到的,直接莽了一发。死活过不了样例。吃完饭回来去hdu重新读题发现数据有个T组。真的是……被自己蠢哭了,orz.

注意如果是从dp[X]往dp[1]转移,需要注意i*k的时候可能会爆int。然后就会RE。

AC代码

#include <cstdio>
#include <algorithm>
#include <cstring>
#define INF 0x3f3f3f3f
typedef long long ll;
using namespace std;

const int maxn=1e6+7;
int X,k,t;
int que[maxn];
int dp[maxn];

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d%d",&X,&k,&t);

        int L=1,R=0;
        memset(dp,0x3f,sizeof(dp));
        dp[X]=0;
        que[++R]=X;
        for(int i=X-1; i>=1; i--)
        {
            while(L<=R && que[L]>i+t) L++;
            if(L<=R) dp[i]=dp[que[L]]+1;
            //注意用long long防止RE
            if((ll)i*k<=(ll)X) dp[i]=min(dp[i],dp[i*k]+1);
            while(L<=R && dp[que[R]]>=dp[i]) R--;
            que[++R]=i;
        }
        printf("%d\n",dp[1]);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_37685156/article/details/81434216