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; }