传送门:http://acm.split.hdu.edu.cn/showproblem.php?pid=5945
描述:
青年理论计算机科学家Fxx给的学生设计了一款数字游戏。 一开始你将会得到一个数X,每次游戏将给定两个参数k,t, 任意时刻你可以对你的数执行下面两个步骤之一: 1.X=X−i(1<=i<=t)。 2.若X为k的倍数,X=X/k。 现在Fxx想要你告诉他最少的运行步骤,使X变成1。
思路:
设dp[i]为i变为1的最小步骤,
当i%k==0时 dp[i]=min(dp[i / k],dp[j])+1{i-t<=j<=i-1}
否则 dp[i]=min(dp[j])+1{i-t<=j<=i-1}
min(dp[j])相当于维护区间[i-t,i-1]最小值。如果纯暴力,复杂度会有 O(N2) ;在这个题目里面, 线段树还是会超时,复杂度为 O(Xlog2(X)) 。如果考虑用单调队列来优化,复杂度为 O(N) 。
代码:
#include <bits/stdc++.h>
using namespace std;
#define mst(ss,b) memset(ss,b,sizeof(ss));
#define rep(i,k,n) for(int i=k;i<=n;i++)
const int inf=0x3f3f3f3f;
const int N=1e6+10;
int deq[N];//单调队列优化,维护下标
int dp[N], x, k, t;
void solve(){
int l, r;
l = r = 1; dp[1] = 0; deq[1] = 1;
rep(i, 2, x){
while(l <= r && deq[l] < i - t)l++; //单调队列中最多只能有t个元素
if(l <= r)dp[i] = dp[deq[l]] + 1;
if(i % k == 0)dp[i] = min(dp[i], dp[i / k] + 1);
while(l <= r && dp[deq[r]] >= dp[i])r--; //保持单调性
deq[++r] = i;
}
}
int main(){
int T;
scanf("%d", &T);
while(T--){
scanf("%d%d%d", &x, &k, &t);
mst(dp, inf);
solve();
printf("%d\n", dp[x]);
}
return 0;
}