0x57 内容简介与例题习题

《算法竞赛进阶指南》读书笔记汇总
这里面是我在阅读《算法竞赛进阶指南》这本书时的一些思考,有兴趣可以瞧瞧!
如若发现什么问题,可以通过评论或者私信作者提出。希望各位大佬不吝赐教!
许多问题都可以使用二叉堆来进行优化,下面直接看例题吧。

基本思想

倍增优化dp主要包括这样两个步骤:预处理和二进制拼凑答案。

【例题】开车旅行(AcWing293)

题目链接
思路:
这道题非常麻烦非常麻烦非常麻烦!
首先是预处理部分,我们预处理出以下数组:
在这里插入图片描述
预处理方式:
在这里插入图片描述
预处理出五个数组之后,我们就可以利用这些已知信息进行二进制拼凑,也就是倍增求解问题。
我们先看问题二,我们从大到小枚举旅行的距离(二进制枚举),判断当前A、B已经走的距离加上枚举的距离是否小于等于给定的 X X X,即将 X X X做二进制拆分。拆分过程中利用已经预处理好的数组计算AB分别走过的距离即可。
对于问题一,我们可以枚举所有的起点,更新最小值即可。

AC代码:

#include <algorithm>
#include <iostream>
#include <cstring>
#include <set>
#include <vector>

using namespace std;

typedef long long LL;
typedef pair<LL, int> PLI;

const int N = 100010, M = 17;
const LL INF = 1e12;

int n;
int h[N];
int ga[N], gb[N];
int f[M][N][2];
LL da[M][N][2], db[M][N][2];

void init_g()
{
    set<PLI> S;
    S.insert({INF, 0}), S.insert({INF + 1, 0});
    S.insert({-INF, 0}), S.insert({-INF - 1, 0});
    
    for (int i = n; i; i -- )
    {
        PLI t(h[i], i);
        auto j = S.lower_bound(t);
        j ++;
        
        vector<PLI> v;
        for (int k = 0; k < 4; k ++ )
        {
            v.push_back(*j);
            j --;
        }
        
        LL d1 = INF, d2 = INF;
        int p1 = 0, p2 = 0;
        for (int k = 3; k >= 0; k --)
        {
            LL d = abs(h[i] - v[k].first);
            if (d < d1)
            {
                d2 = d1, d1 = d;
                p2 = p1, p1 = v[k].second;
            }
            else if (d < d2)
            {
                d2 = d;
                p2 = v[k].second;
            }
        }
        ga[i] = p2, gb[i] = p1;
        S.insert(t);
    }
}

void init_f()
{
    for (int i = 0; i < M; i ++ )
        for (int j = 1; j <= n; j ++ )
        {
            if (!i) f[0][j][0] = ga[j], f[0][j][1] = gb[j];
            else
            {
                for (int k = 0; k < 2; k ++ )
                {
                    if (i == 1) f[1][j][k] = f[0][f[0][j][k]][1 - k];
                    else f[i][j][k] = f[i - 1][f[i - 1][j][k]][k];
                }
            }
        }
}

int dist(int a, int b)
{
    return abs(h[a] - h[b]);
}

void init_d()
{
    for (int i = 0; i < M; i ++ )
        for (int j = 1; j <= n; j ++ )
        {
            if (!i) 
            {
                da[0][j][0] = dist(j, ga[j]), da[0][j][1] = 0;
                db[0][j][1] = dist(j, gb[j]), db[0][j][0] = 0;
            }
            else
            {
                for (int k = 0; k < 2; k ++ )
                {
                    if (i == 1)
                    {
                        da[1][j][k] = da[0][j][k] + da[0][f[0][j][k]][1 - k];
                        db[1][j][k] = db[0][j][k] + db[0][f[0][j][k]][1 - k];
                    }
                    else
                    {
                        da[i][j][k] = da[i - 1][j][k] + da[i - 1][f[i - 1][j][k]][k];
                        db[i][j][k] = db[i - 1][j][k] + db[i - 1][f[i - 1][j][k]][k];
                    }
                }
            }
        }
}

void calc(int x, int s, int &la, int &lb)
{
    la = lb = 0;
    for (int i = M - 1; i >= 0; i -- )
    {
        if (f[i][s][0] && la + lb + da[i][s][0] + db[i][s][0] <= x)
        {
            la += da[i][s][0], lb += db[i][s][0];
            s = f[i][s][0];
        }
    }
}

int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; i ++ ) scanf("%d", &h[i]);
    
    init_g();
    init_f();
    init_d();
    
    int s, x;
    scanf("%d", &x);
    int ans = 0, max_h = 0;
    double min_ratio = INF;
    for (int i = 1; i <= n; i ++ )
    {
        int la, lb;
        calc(x, i, la, lb);
        double ratio = lb ? 1.0 * la / lb : INF;
        if (ratio < min_ratio || ratio == min_ratio && max_h < h[i])
        {
            min_ratio = ratio;
            max_h = h[i];
            ans = i;
        }
    }
    
    printf("%d\n", ans);
    
    int m;scanf("%d", &m);
    while(m -- )
    {
        scanf("%d%d", &s, &x);
        int la, lb;
        calc(x, s, la, lb);
        printf("%d %d\n", la, lb);
    }
    
    return 0;
}

【例题】计算重复(AcWing294)

题目链接
思路:我们考虑这样一个问题,计算出 c o n n ( s 1 , n 1 ) conn(s1, n1) conn(s1,n1)生成的 c o n n ( s 2 , n 2 ) conn(s2, n2) conn(s2,n2)的数量 m ′ m' m,那么原问题的解就是 m = m ′ / n 2 m = m' / n2 m=m/n2
同样是一个倍增的问题,可以通过预处理和二进制拼凑来求解。
首先预处理这样一个数组:
在这里插入图片描述
然后倍增过程也是比较简单滴

AC代码:

#include <iostream>
#include <algorithm>
#include <cstring>

using namespace std;

typedef long long LL;

const int N = 110, M = 31;

int n1, n2;
string s1, s2;
LL f[N][M];

void solve()
{
    int sz = s1.size();
    bool flag = false;
    for (int i = 0; i < sz; i ++ )
    {
        f[i][0] = 0;
        int p = i;
        for (int j = 0; j < s2.size(); j ++ )
        {
            int cnt = 0;
            while(s1[p] != s2[j])
            {
                p = (p + 1) % sz;
                cnt ++;
                if (cnt >= sz)
                {
                    flag = true;
                    break;
                }
            }
            p = (p + 1) % sz;
            f[i][0] += cnt + 1;
            if (flag) break;
        }
        if (flag) break;
    }
    if (flag)
    {
        cout << 0 << endl;
        return ;
    }
    
    for (int j = 1; j < M; j ++ )
        for (int i = 0; i < sz; i ++ )
            f[i][j] = f[i][j - 1] + f[(i + f[i][j - 1]) % sz][j - 1];

    LL ans = 0;
    for (int i = 0; i < sz; i ++ )
    {
        LL p = i, t = 0;
        for (int j = M - 1; j >= 0; j -- )
        {
            if (p + f[p % sz][j] <= sz * n1)
            {
                p += f[p % sz][j];
                t += 1 << j;
            }
        }
        ans = max(ans, t);
    }
    
    cout << ans / n2 << endl;
}

int main()
{
    while(cin >> s2 >> n2 >> s1 >> n1)
    {
        solve();
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值