题意:给3个数x, k, t。经一系列操作后,使x变为1,求最少的操作次数。具体操作为:每次x可以减去一个数0~t,或者除以k。
解析:本题使用dp,但是需要用单调队列进行优化。
动规需要从1开始,一直往上计算,直到x为止。
递归公式为:dp[i]=min(min(dp[i-t]~dp[i-1])+1,dp[i/k]+1)
单调队列:单调队列中的数字都是递增或者递减的。本题中队列用来保存下标,队首的下标总是对应i-1~i-t中次数最小的那个数的下标。
单调队列是指:队列中元素之间的关系具有单调性,而且,队首和队尾都可以进行出队操作,只有队尾可以进行入队操作。
以单调不减队列为例:队列内的元素(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);
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e6 + 10;
#define INF 0x3f3f3f3f
typedef pair<int,int> P;
typedef long long ll;
int dp[maxn];
int temp[maxn];
int main()
{
int Tcase;
scanf("%d",&Tcase);
while(Tcase --)
{
int x,k,t;
scanf("%d%d%d",&x,&k,&t);
int l = 1,r = 1;dp[1] = 0;temp[r] = 1;
for(int i = 2; i <= x; i ++)
{
dp[i] = INF;
while(l <= r && temp[l] < i - t)l ++;
if(l <= r)dp[i] = dp[temp[l]] + 1;
if(i % k == 0)dp[i] = min(dp[i],dp[i / k] + 1);
while(l <= r && dp[temp[r]] >= dp[i])r --;
temp[++ r] = i;
}
printf("%d\n",dp[x]);
}
return 0;
}