题目链接
http://acm.hdu.edu.cn/showproblem.php?pid=5945
题目
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,有两种操作:
1.X=X-i(0<=i<=t)
2.if k|X,X=X/k。
求最少经过多少次操作能将X变成1.
解题
设dp[i]表示将i变成1需要的最少操作次数。
那么,dp[i]=min{dp[i*k],dp[i+j]+1},0<=j<=t.
如果直接按照状态转移方程去写,时间复杂度是O(n^2).
需要用更快的方法求出min{dp[i+j]},0<=j<=t.
而求一个滑动窗口的最小值显然是单调队列的拿手好戏。
维护一个单调上升的队列即可。
这题是在别人博客里看到的,直接莽了一发。死活过不了样例。吃完饭回来去hdu重新读题发现数据有个T组。真的是……被自己蠢哭了,orz.
注意如果是从dp[X]往dp[1]转移,需要注意i*k的时候可能会爆int。然后就会RE。
AC代码
#include <cstdio>
#include <algorithm>
#include <cstring>
#define INF 0x3f3f3f3f
typedef long long ll;
using namespace std;
const int maxn=1e6+7;
int X,k,t;
int que[maxn];
int dp[maxn];
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d%d%d",&X,&k,&t);
int L=1,R=0;
memset(dp,0x3f,sizeof(dp));
dp[X]=0;
que[++R]=X;
for(int i=X-1; i>=1; i--)
{
while(L<=R && que[L]>i+t) L++;
if(L<=R) dp[i]=dp[que[L]]+1;
//注意用long long防止RE
if((ll)i*k<=(ll)X) dp[i]=min(dp[i],dp[i*k]+1);
while(L<=R && dp[que[R]]>=dp[i]) R--;
que[++R]=i;
}
printf("%d\n",dp[1]);
}
return 0;
}