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=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,对于这个数x,我们有两种变化,:
1,我们可以让x减去一个数y,其中 0<=y<=t
2,如果x是k的倍数,那么我们可以让x=x/k
每一次我们可以选择以上两种操作之一,问从x到1的最小变化次数是多少。
解题思路:这个题我们可以很容易的推出状态转移公式,
dp[j]=min(dp[i])+1 (其中0<=j-i<=t)
if(j%k==0) dp[j]=min(dp[j],dp[j/k]+1);
我们很容易就可以推导出以上式子,但是发现遍历i时的复杂度为O(N)的,而t是一个小于等于1e6的数,所以暴力肯定会T的,这里我们就要用到单调队列优化啦。单调队列,就是存储的值是具有单调性的队列,所以我们可以通过维护这个单调队列来让我们可以O(1)的取出这个最小值,但是又有一个问题,怎么保证j-i<=t呢?我们可以用一个头指针来保存现在的单调队列的开始位置,用一个id数组来保存队列中的值出现的位置,如果队列中的值所在的位置不能满足可以通过相减来得到时,就将头指针向后移一位,保证我们所求的单调队列中的最小值就是前t项的最小值。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <string>
#include <vector>
#include <set>
#include <map>
#include <bitset>
using namespace std;
#define int long long
const int maxn = 1e6 + 7;
const int inf = 0x3f3f3f3f;
int st[maxn],top,head,id[maxn];
int dp[maxn];
signed main() {
int T;
cin>>T;
while(T--) {
memset(dp,inf,sizeof dp);
dp[1]=0;
int x,k,t;
head=top=0;
memset(st,0,sizeof st);
memset(id,0,sizeof id);
id[++top]=1;
scanf("%lld%lld%lld",&x,&k,&t);
for(int i=2; i<=x; i++) {
if(i%k==0)dp[i]=dp[i/k]+1;
while(i-id[head+1]>t && top>head)head++;
if(head<top) dp[i]=min(dp[i],st[head+1]+1);
while(st[top]>=dp[i] && top>head) {
top--;
}
st[++top]=dp[i],id[top]=i;
}
printf("%lld\n",dp[x]);
}
}