HDU 4518 解题报告

1到1e11的斐波那契数很少,显然可以预处理出来。
然后枚举这些数,假设其为k,那么可以利用二分数位dp去查找第k个F数,calc(x)就是要求出1-x有多少个F数。
这里的问题是,怎么表示状态。
因为斐波那契数是字符串,而且数位dp是一个字符一个字符加上去的,无论是用map映射,还是hash数组,都不好转移,我们利用ac自动机里面,每个单词的结尾部分的节点值就表示这个单词,那么状态就完美解决了,惊奇的是,在数位dp做转移的时候也方便许多。
然后状态就是:dp[i][j][]。前i位,当前走到j节点,是否已经包含斐波那契数。

/*
纳兰性德 -清
《长相思·山一程》
山一程,水一程,身向榆关那畔行,夜深千帐灯。
风一更,雪一更,聒碎乡心梦不成,故园无此声。
*/
//  Created by Matrix on 2016-02-02
//  Copyright (c) 2015 Matrix. All rights reserved.
//
//
//#pragma comment(linker, "/STACK:102400000,102400000")
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define ALL(x) x.begin(), x.end()
#define INS(x) inserter(x, x,begin())
#define ll long long
#define CLR(x) memset(x, 0, sizeof x)
using namespace std;
const ll inf = 0x3f3f3f3f3f3f3f3f;
const int MOD = 1e9 + 7;
const int maxn = 1e6 + 10;
const int maxv = 1e3 + 10;
const double eps = 1e-9;
const int Size = 10;
int nxt[maxv][10];
int have[maxv], fail[maxv];
struct AcAmount {
    int cnt;
    int root;
    int newNode() {
        for(int i = 0; i < Size; i++) {
            nxt[cnt][i] = -1;
        }
        have[cnt++] = 0;
        return cnt - 1;
    }
    void init() {
        cnt = 0;
        root = newNode();
    }
    void insert(ll k) {
        int digit[20];
        int len = 0;
        int now = root;
        while(k) {
            digit[len++] = k % 10;
            k /= 10;
        }
        for(int i = len - 1; i >= 0; i--) {
            int num = digit[i];
            if(nxt[now][num] == -1) nxt[now][num] = newNode();
            now = nxt[now][num];
        }
        have[now] = 1;
    }
    void build() {
        fail[root] = root;
        queue <int> que;
        for(int i = 0; i < Size; i++) {
            if(nxt[root][i] == -1) {
                nxt[root][i] = root;
            }
            else {
                fail[nxt[root][i]] = root;
                que.push(nxt[root][i]);
            }
        }
        while(que.size()) {
            int u = que.front();
            que.pop();
            for(int i = 0; i < Size; i++) {
                int &v = nxt[u][i];
                if(v == -1) {
                    v = nxt[fail[u]][i];
                }
                else {
                    fail[v] = nxt[fail[u]][i];
                    have[v] |= have[fail[v]];
                    que.push(v);
                }
            }
        }
    }
};
ll fab[100];
int tot;
ll dp[20][maxv][2];
ll digit[20];
void Init() {
    AcAmount ac;
    ac.init();
    tot = 2;
    fab[1] = 1;
    fab[2] = 1;
    for(int i = 3; i < 100; i++) {
        if(fab[i-1] + fab[i-2] > (ll)2e11) break;
        fab[++tot] = fab[i-1] + fab[i-2];
//      cout << fab[i] << endl;
    }
//  cout << tot <
    for(int i = 1; i <= tot; i++) {
        if(fab[i] < 10) continue;
        ac.insert(fab[i]);
    }
    ac.build();
    memset(dp, -1, sizeof dp);
}


ll dfs(int step, int pos, int flag, int limit) {
    if(!step) return flag > 0;
    if(!limit && dp[step][pos][flag] != -1) return dp[step][pos][flag];
    ll ret = 0;
    int up = limit ? digit[step] : 9;
    for(int i = 0; i <= up; i++) {
        int j = nxt[pos][i];
        ret += dfs(step-1, j, have[j] | flag, limit && i == up);
    }
    if(!limit) dp[step][pos][flag] = ret;
    return ret;
}
ll Calc(ll n) {
    int cnt = 0;
//  memset(dp, -1, sizeof dp);
    while(n) {
        digit[++cnt] = n % 10;
        n /= 10;
    }
//  cout << tm << "********" << dfs(cnt, 0, 1) << endl;
    return dfs(cnt, 0, 0, 1);
}
ll Find(ll n) {
    ll l = 1, r = (ll)4e12;
    while(l <= r) {
        ll mid = (l + r) / 2;
        ll num = Calc(mid);
//        printf("mid = %lld  ", mid);
//        printf("num = %lld\n", num);

        if(num >= n) r = mid - 1;
        else l = mid + 1;
//        printf("l = %lld  r = %lld\n", l, r);
    }
//    printf("n = %lld  r = %lld\n", n, r);
    return r + 1;
}
int main() {
#ifdef LOCAL
    freopen("in.txt", "r", stdin);
//  freopen("out.txt","w",stdout);
#endif
    Init();
    ll n;
//  for(int i = 12; i <= 40; i++) {
//          printf("i = %d  calc = %lld\n", i, Calc(i));
//      }
    while(scanf("%I64d", &n) != EOF && n != -1) {
        ll ans = inf;
        for(int i = 2; i <= tot; i++) {
            ans = min(ans, abs(n - Find(fab[i])));
        }
        printf("%I64d\n", ans);
//      for(int i = 1; i <= 3; i++) {
//          printf("%d  %I64d\n", i, Find(i));
//      }
//      for(int i = 12; i <= 40; i++) {
//          printf("i = %d  calc = %lld\n", i, Calc(i));
//      }
    }
    return 0;
}

你可能感兴趣的:(动态规划字符串)