HDU-5945 Fxx and game (单调队列与BFS)

借鉴自:http://blog.csdn.net/queuelovestack/article/details/52970866

Fxx and game

 
 Accepts: 74
 
 Submissions: 1857
 Time Limit: 3000/1500 MS (Java/Others)
 
 Memory Limit: 131072/65536 K (Java/Others)
问题描述
青年理论计算机科学家Fxx给的学生设计了一款数字游戏。

一开始你将会得到一个数XX,每次游戏将给定两个参数k,tk,t, 任意时刻你可以对你的数执行下面两个步骤之一:

1.X=X−i(1<=i<=t)1.X=Xi(1<=i<=t)2.2.XXkk的倍数,X=X/kX=X/k。

现在Fxx想要你告诉他最少的运行步骤,使XX变成11
输入描述
第一行一个整数T(1≤T≤20)T(1T20)表示数据组数。

接下来TT行,每行三个数X,k,t(0≤t≤106,1≤X,k≤106)X,k,t(0t106,1X,k106)

数据保证有解。
输出描述
输出共TT行。

每行一个整数表示答案。
输入样例
2
9 2 1
11 3 3
输出样例
4
3

分析:做题时错误使用了贪心导致wa了十分智障。阅读别人博客发现有bfs与单调队列两种做法。
BFS做法:遍历状态,将当前状态所能达到的所有可能状态压栈遍历。进行适当剪枝,遍历过的无需再次遍历即可。
单调队列做法:用单调队列维护可选状态,

1002 Fxx and game

FiFi表示将ii变为11的最少步骤,如果k∣i,Fi=min{Fj,Fik}ki,Fi=min{Fj,Fki},否则Fi=min{Fj}Fi=min{Fj},其中i−t≤j≤i−1itji1

用单调队列维护一下就好了。

时间复杂度O(n)O(n)

题解写的十分简略,但意思挺明显的,维护单调队列的意义是为求出当前状态的最优解做准备,不过做题时并没有想到单调队列,用的也很少。
//BFS
#include <algorithm>
#include <iostream>
#include <sstream>
#include <cstring>
#include <cstdlib>
#include <string>
#include <vector>
#include <cstdio>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <set>
using namespace std;
#define INF 0x3f3f3f3f
const long long N=1000005;
const long long mod=1e9;
const double PI=acos(-1.0);
typedef long long ll;

struct node {
    int x,c;
    node(){};
    node(int _x,int _c):x(_x),c(_c){}
};

bool v[N];

int bfs(int x,int k,int t) {
    if (k==1&&t) {
        return (x+t-2)/t;
    }
    int minn;
    node u;
    queue<node> q;
    for (int i=1; i<=x; i++) {
        v[i]=false;
    }
    q.push(node(x,0));
    v[x]=true;
    while (!q.empty()) {
        u=q.front();
        q.pop();
        if (u.x==1) {
            return u.c;
        }
        if (u.x%k==0&&!v[u.x/k]) {
            q.push(node(u.x/k, u.c+1));
            v[u.x/k]=true;
        }
        minn=min(t,u.x-1);
        for (int i=minn; i>=1; i--) {
            if (!v[u.x-i]) {
                q.push(node(u.x-i, u.c+1));
                v[u.x-i]=true;
            }
            else break;
        }
    }
    return 0;
}

int main() {
    int ti;
    cin>>ti;
    int x,t,k;
    while (ti--) {
        cin>>x>>k>>t;
        cout<<(bfs(x, k, t))<<endl;
    }
    return 0;
}

//单调队列
#include <algorithm>
#include <iostream>
#include <sstream>
#include <cstring>
#include <cstdlib>
#include <string>
#include <vector>
#include <cstdio>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <set>
using namespace std;
#define INF 0x3f3f3f3f
const long long N=1000005;
const long long mod=1e9;
const double PI=acos(-1.0);
typedef long long ll;

int ans[N],p[N];

int main() {
    int ti;
    cin>>ti;
    int x,t,k;
    while (ti--) {
        cin>>x>>k>>t;
        if (k==1&&t) {
            cout<<(x+t-2)/t<<endl;
            continue;
        }
        int head,tail;
        ans[1]=0;p[1]=1;head=tail=1;
        for (int i=2; i<=x; i++) {
            if (i%k==0) {
                ans[i]=ans[i/k]+1;
            } else {
                ans[i]=INF;
            }
            while (head<=tail&&i-p[head]>t) {
                head++;
            }
            if (head<=tail) {
                ans[i]=min(ans[i], ans[p[head]]+1);
            }
            while (head<=tail&&ans[p[tail]]>=ans[i]) {
                tail--;
            }
            p[++tail]=i;
        }
        cout<<ans[x]<<endl;
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值