题目链接
本题为BestCoder Round#89 1002题
HDU 5945
中文版本
题目大意
求数x经过多少次变换能变为1。变换有以下两种:
1.如果x%k==0,那么可以x=x/k。
2.x=x-i,(1
≤
i
≤
t)
(0
≤
t
≤
106
,1
≤
x,k
≤
106
)
分析
这是一道线性dp,分析后易得状态转移方程
f(n)=
{min(dp[i−t],...,dp[i−1])+1,min(min(dp[i−t],...,dp[i−1])+1,dp[i/k]+1),i%k!=0i%k==0
用单调队列维护min(dp[i-t],…,dp[i-1])即可,t相当于滑动窗口长度,思想详见poj2823。
代码
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#define N 1000010
#define Min(a,b) ((a<b)?a:b)
using namespace std;
int Q[N],dp[N];
int main()
{
int T,t,x,k,cnt,i,head,tail;
scanf("%d",&T);
while (T--)
{
scanf("%d%d%d",&x,&k,&t);
cnt=0;
if (t==0) //考虑只有除法的特例
{
while (x!=1)
{
x/=k;
cnt++;
}
printf("%d\n",cnt);
continue;
}
if (k==0) //考虑只有减法的特例
{
if ((x-1)%t==0)
cnt=(x-1)/t;
else
cnt=(x-1)/t+1;
printf("%d\n",cnt);
continue;
}
///dp[i]=min(min(dp[i-t]~dp[i-1])+1,dp[i/k]+1)
dp[1]=0;
head=tail=1;
Q[1]=1;
for (i=2;i<=x;i++)
{
while ((head<=tail)&&(Q[head]<i-t))
head++; //删除过时队首元素
dp[i]=dp[Q[head]]+1;
if (i%k==0)
dp[i]=Min(dp[i],dp[i/k]+1);
while ((head<=tail)&&(dp[i]<=dp[Q[tail]]))
tail--; //删除队尾元素
Q[++tail]=i; //入队
}
printf("%d\n",dp[x]);
}
return 0;
}