HDU5945 Fxx and game 单调队列优化dp

题目链接:

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

题意:

求数x最少经过多少次变换能变为1,(1)如果x%k==0,那么可以x=x/k。(2)x=x-i,(1<=i<=t)

思路:

动规需要从1开始,一直往上计算,直到x为止。

递归公式为:dp[i]=min(min(dp[i-t]~dp[i-1])+1,dp[i/k]+1)

min(dp[i-t]~dp[i-1])这个需要维护一个递增的单调队列优化,使得复杂度降为O(1). 只有当前满足范围的最小的dp值是对后面的更新有贡献的,如果当前的dp值比队尾还小,那么这个队尾就没用了,因为用当前值更新后面肯定比队尾更符合范围而且更小。

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define MS(a) memset(a,0,sizeof(a))
#define MP make_pair
#define PB push_back
const int INF = 0x3f3f3f3f;
const ll INFLL = 0x3f3f3f3f3f3f3f3fLL;
inline ll read(){
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
//////////////////////////////////////////////////////////////////////////
const int maxn = 1e6+10;

int dp[maxn],que[maxn];

int main(){
    int T = read();
    while(T--){
        int x,k,t; scanf("%d%d%d",&x,&k,&t);
        int head=1,tail=0;
        memset(dp,INF,sizeof(dp));
        dp[1] = 0;
        for(int i=1; i<=x; i++){
            if(i%k == 0) dp[i] = min(dp[i],dp[i/k]+1);
            dp[i] = min(dp[i],dp[que[head]]+1);
            // cout << head << " " << dp[head] << " " << i << " " << dp[i] << endl;
            // for(int i=head; i<=tail; i++){
            //     cout << que[i] << " ";
            // }
            // puts("");
            // puts("=====");
            while(head<=tail && dp[i]<=dp[que[tail]]) tail--;
            que[++tail] = i;
            while(head<=tail && i-que[head]>=t) head++;
        }
        cout << dp[x] << endl;
    }

    return 0;
}

 

posted @ 2017-09-03 20:19  _yxg123  阅读(151)  评论(0编辑  收藏  举报