【HDU 5945】Fxx and game(DP+单调队列)

【HDU 5945】Fxx and game(DP+单调队列)

啊啊啊啊啊啊啊。。。BC第二题就这么难了……好菜啊……这游戏没法玩了啊……。。。

膜一下队伍主力 金QAQ巨。。。现两号BC金。。Orz……@a1s4z5

顺吐槽一发……大家BC页面会卡么……宿舍进BC卡得要死……具体卡成什么样子……校园网进不去热点也卡的要命,耐心等待题面一帧帧 浮现 出来……然后已经被甩出去好几条街了……然后……咦……这A题坑点很迷啊,Hack肯定很爽啊,耐心等待页面 一条条 出现后……双击room里一人的A题……我选择放弃。。。。

从此BC成路人……

言归正传,这题还是很好的……即使因此分掉了一大截……摔。。

具体的:给出x,t,k,要将数字x变成1。操作有二:
1.把数字x变成数字y(0 <= x-y <= t),消耗一步。
2.x->x/k(if x%k == 0),消耗一步。

一开始的想法很犀利……定义 dp[i] 为从x变成i的最少步数,那么答案其实是 min(dp[i])1<=i<k ,因为到k的话肯定会/k变成1,其实就是dp[1],所以只考虑1~k-1。

然后对于i(1 <= i < k),找到最大的y,满足y%k == 0 && y/k == i && y <= x
然后计算从x变成y,然后从y变成i,从i变成1。前后除t即可,中间就有点暴力了。。然后就爆炸了……然后刚才发现有地方写搓了。。然后改了后测了一些数据发现这种贪心是有问题的……然后还是爆炸了……

还是规规矩矩的做法吧,dp[i]表示i变成1的最少步数。
那么如果i%k != 0 那么dp[i] = min(dp[j])+1(0 <= i-j <= t)
如果i%k == 0,最小值里再取个dp[i/k]。

暴力的话会超时,维护一个跟i距离t内的单调队列,队列里的数值从左到右单调递增,右边入队左边取值即为t内最少步数。

代码如下:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define LL long long
#define Pr pair
#define fread(ch) freopen(ch,"r",stdin)
#define fwrite(ch) freopen(ch,"w",stdout)

using namespace std;
const int INF = 0x3f3f3f3f;
const int mod = 1e9+7;
const double eps = 1e-8;
const int maxn = 1123456;

int s[maxn];
int dp[maxn];

int main()
{
    //fread("");
    //fwrite("");

    int t,T,l,r,x,k;

    scanf("%d",&T);

    while(T--)
    {
        scanf("%d%d%d",&x,&k,&t);

        dp[1] = 0;
        l = 0;
        r = -1;

        s[++r] = 1;

        for(int i = 2; i <= x; ++i)
        {
            if(i%k == 0 && k != 1) dp[i] = dp[i/k]+1;

            if(l <= r) 
            {
                if(i%k == 0 && k != 1) dp[i] = min(dp[i],dp[s[l]]+1);
                else dp[i] =dp[s[l]]+1;
            }

            while(l <= r && dp[i] <= dp[s[r]])
                r--;

            s[++r] = i;

            while(l <= r && i-s[l] >= t) ++l;
        }

        printf("%d\n",dp[x]);
    }

    return 0;
}

你可能感兴趣的:(DP,HDOJ,ACM之DP)