[hdu 5945 Fxx and game] dp+单调队列
题目链接:[hdu 5945 Fxx and game]
题意描述:请看BestCoder中文题面…传松门
青年理论计算机科学家Fxx给的学生设计了一款数字游戏。
一开始你将会得到一个数
X
,每次游戏将给定两个参数
- X=X−i(1<=i<=t);
- 若X为k的倍数,X=X/k。
现在Fxx想要你告诉他最少的运行步骤,使
X
变成
解题思路:用
dp[z]
表示数
z
变换到
dp[z]={min{dp[z−i]}+1,min{dp[z−i],dp[z/k]}+1,(z%k≠0)(z%k=0)(其中,1≤i≤t).
求 min{dp[z−i]}(1≤i≤t) 就相当于求区间 [z−t,z] 的区间最小值。纯暴力,复杂度会有 O(N2) ;在这个题目里面, 线段树还是会超时,复杂度为 O(Xlog2(X)) 。如果考虑用单调队列来优化,复杂度为 O(N) 。
单调队列的队首永远是 dp 最小值。
#include <bits/stdc++.h>
using namespace std;
int T, X, k, t;
const int MAXN = 1e6 + 5;
const int INF = 0x3f3f3f3f;
queue<int> Q;
int dp[MAXN];
int main() {
// freopen("E:\\ACM\\input.txt", "r", stdin);
scanf("%d", &T);
while(T --) {
scanf("%d %d %d", &X, &k, &t);
memset(dp, 0x3f, sizeof(dp));
while(!Q.empty()) Q.pop();
dp[1] = 0;
Q.push(1);
for(int z = 1; z <= X; z++) {
while(!Q.empty() && Q.front() < z - t) Q.pop();
if(!Q.empty()) dp[z] = min(dp[z], dp[Q.front()] + 1);
if(z % k == 0) dp[z] = min(dp[z], dp[z / k] + 1);
while(!Q.empty() && dp[Q.front()] >= dp[z]) Q.pop();
Q.push(z);
}
printf("%d\n", dp[X]);
}
return 0;
}