题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5945

HDOJ 5945 Fxx and game_ios

题意:给我们k,t,我们每次能把一个数减去一个小于等于t的数,如果当前数是k的倍数,可以除以k,问我们将X变为1最少需要的操作数。

首先,这题小编第一反应是贪心,当时贪心的想法就是能除尽量除,如果不能除就减到可以除,交上去直接A了也没多想,结果比赛结束不到一分钟就被hack了。那么我们先来分析一下贪心为什么不对,我们举个例子,比如说9 4 5这组数据,如果我们按这个贪心策略的话就是先减去1,使得变成4的倍数8,再除4,再减一,3步,但是实际上我们直接连续减两次就直接能够达到我们的要求。

那么我们就可以考虑dp来解决,用dp[i]表示i这个数变成1最少需要的次数的话,那么dp[i] = min(dp[i-1]+1, dp[i-2]+1……dp[i-t]+1, dp[i/k]+1),考虑到我们需要得到前t项里的最小值,我们可以使用单调队列。


#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 1e6 + 10;
int T,n,m,x,k,t,d[maxn];
struct node
{
int id, v;
node(int id=0, int v=0):id(id), v(v) {}
}a[maxn];
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d%d%d", &x, &k, &t);
memset(d, 0x3f, sizeof(d));
d[1] = 0;
int l = 0, r = 0;
a[0] = node(1, 0);
for(int i = 2; i <= x; i++)
{
if(i % k == 0) d[i] = min(d[i], d[i/k]+1);
while(a[l].id < i-t && l <= r) l++;
if(l > r)
{
l--;
a[l] = node(i, d[i]);
continue;
}
d[i] = min(d[i], a[l].v+1);
while(d[i] < a[r].v && r >= l) r--;
a[++r] = node(i, d[i]);
}
printf("%d\n", d[x]);
}
return 0;
}