【LuoguP6772】美食家【DP】【矩阵】【倍增】

链接

题目大意:

有n个点,m条边,每个点有个权值c,每条边有长度w,要求刚好走过长度总共为T的路(可重复),从1号点回到1号点,最大化选择路径上每个点的权值的总和。

其中还有k个,在刚好走过t的长度时,在点u会有额外的权值v。

思路:

很容易想到状态为 f i u f_{i \ u} fi u的转移,表示走过i长度时,刚好在点u的最大权值,但时间复杂度显然不够。

假设k=0

考虑矩阵加速优化,原来的转移方程: f i v = m a x ( f i − w u + c v ) f_{i \ v} = max(f_{i - w \ u} + c_v) fi v=max(fi−w u+cv)

这里使用一个巧妙地思路,将每个点u拆成5个点(w的最大值), u i u_i ui向 u i + 1 u_{i+1} ui+1连一条边权为0的边,对于原图的每条边,从 u w u_w uw向 v 1 v_1 v1连一条边权为c的边,这样可以保证每次不能停在原地,且巧妙地将c融入矩阵之中,方便后续的乘法操作。(n *= 5)

然后根据转移方程,这里的矩阵加速就是 c i j = m a x ( a i k + b k j ) c_{i \ j} = max(a_{i \ k} + b_{k \ j}) ci j=max(ai k+bk j),用矩阵快快速幂实现。

k不等于0

相当于将从0到T的一整次矩阵快速幂分成k次来做,每次只需要在中断处将额外权值加入ans就可以。

这里还需要一个优化。先将矩阵指数幂预处理出来。每次从last->now的长度时,将now-last进行指数分解( 2 a + 2 b + ... ... ) 2^a+2^b+......) 2a+2b+......),每次只需要从预处理出的指数幂中拿出矩阵进行ans更新,不需要log(now - last)在算一次。

时间复杂度: O ( l o g T ( 5 n ) 3 + k l o g T ( 5 n ) 2 ) O(log \ T \ (5n)^3 + k \ log \ T \ (5n)^2) O(log T (5n)3+k log T (5n)2)

code

cpp 复制代码
#include<iostream>
#include<cstdio>
#include<algorithm>

#define ll long long

using namespace std;

const ll MAXN = 505, inf = 1e16;

ll n, m, T, k;
ll C[MAXN], a[MAXN][MAXN], ans[MAXN], b[MAXN];
ll Pow[31][MAXN][MAXN];

struct Reward {
    ll t, u, v;
} t[MAXN];

bool cmp(Reward x, Reward y) {
    return x.t < y.t;
}

void mul_(ll to, ll fro) {
    for(ll i = 1; i <= n; i ++)
        for(ll j = 1; j <= n; j ++) {
            Pow[to][i][j] = -inf;
            for(ll k = 1; k <= n; k ++)
                Pow[to][i][j] = max(Pow[to][i][j], Pow[fro][i][k] + Pow[fro][k][j]);
        }
}

void mul_ans(ll cnt) {
    for(ll i = 1; i <= n; i ++) b[i] = ans[i], ans[i] = -inf;
    for(ll j = 1; j <= n; j ++)
        for(ll k = 1; k <= n; k ++)
            ans[j] = max(ans[j], b[k] + Pow[cnt][k][j]);
}

void init_() {
    for(ll i = 1; i <= n; i ++)
        for(ll j = 1; j <= n; j ++) Pow[0][i][j] = a[i][j];
    for(ll i = 1; i <= 30; i ++) mul_(i, i - 1);
}

void Update(ll B) {
    ll j = 30;
    while(B) {
        if(B >= (1 << j)) mul_ans(j), B -= 1 << j;
        j --;
    }
}

int main() {
    scanf("%lld%lld%lld%lld", &n, &m, &T, &k);
    for(ll i = 1; i <= n; i ++) scanf("%lld", &C[i]);
    n *= 5;
    for(ll i = 1; i <= n; i ++)
        for(ll j = 1; j <= n; j ++) a[i][j] = ans[i] = -inf;
    for(ll i = 1; i <= n / 5; i ++)
        for(ll j = 1; j < 5; j ++) a[(i - 1) * 5 + j][(i - 1) * 5 + j + 1] = 0;
    for(ll i = 1; i <= m; i ++) {
        ll u, v, w;
        scanf("%lld%lld%lld", &u, &v, &w);
        a[(u - 1) * 5 + w][(v - 1) * 5 + 1] = C[v];
    }
    init_();
    for(ll i = 1; i <= k; i ++) scanf("%lld%lld%lld", &t[i].t, &t[i].u, &t[i].v);
    sort(t + 1, t + 1 + k, cmp);
    ans[1] = C[1];
    for(ll i = 1; i <= k; i ++) {
        ll B = t[i].t - t[i - 1].t;
        Update(B);
        ans[(t[i].u - 1) * 5 + 1] += t[i].v;
    }
    if(t[k].t != T) Update(T - t[k].t);
    if(ans[1] < 0) printf("-1");
    else printf("%lld", ans[1]);
    return 0;
}
相关推荐
罗西的思考4 小时前
【OpenClaw具身硬件】MiniClaw 阅读笔记---(1)基础
人工智能·算法·机器学习
蛋先生DX4 小时前
大模型参数存储格式揭秘:BF不是男朋友
深度学习·算法·llm
爱跳舞的烤冷面5 小时前
自学嵌入式第22天(数据结构——哈希)
数据结构·算法·哈希算法
猎嘤一号5 小时前
博弈论(Game Theory)的理论、算法与工程
人工智能·算法·安全·博弈论
月光船幽幽5 小时前
影子模式下保护 logits 不被修改
人工智能·python·算法
马拉AI5 小时前
腾讯开源 Agent 记忆系统,AI“换对话就忘”的问题有了新解法(附安装使用教程)
人工智能·算法·开源·科研
C++ 老炮儿的技术栈6 小时前
从 Qt Designer 属性编辑器的层级可以看到继承链
c语言·数据库·c++·qt·sqlite·visual studio
new_zhou7 小时前
C++ 项目 AI 协作指南(Windows / MSVC 环境)
c++·人工智能·windows
地平线开发者7 小时前
征程6工具链模型X86推理方式说明
算法
地平线开发者7 小时前
【征程6】校准量化中HistogramObserver解析
算法