魔法 [动态规划]

本文探讨了一种使用动态规划解决的魔法问题。定义了状态F[i]表示在位置i切割,且所有区间满足条件的最小花费。通过状态转移方程F[i]=min(F[l]+A[i])找到最优解,其中l是右端点小于i的最大区间左端点。最终答案为所有区间上的最小F值。

魔 法 魔法



正 解 部 分 \color{red}{正解部分}

F [ i ] F[i] F[i] 表示最后一刀切在 i i i 位置, 右端点小于等于 i i i 的所有区间都满足条件的最小花费,

状态转移, 在 i i i 位置切一刀, F [ i ] = min ⁡ ( F [ l 右 端 点 小 于 i 的 区 间 中 右 端 点 最 大 的 区 间 的 左 端 点   . . .   i − 1 ] + A [ i ] ) F[i] = \min(F[l_{右端点小于i的区间中右端点最大的区间的左端点}\ ...\ i-1] + A[i]) F[i]=min(F[li ... i1]+A[i]) .

最后 a n s = min ⁡ ( F [ l 最 后 一 个 区 间 . . . i − 1 ] ) ans = \min(F[l_{最后一个区间} ... i-1]) ans=min(F[l...i1]) .


实 现 部 分 \color{red}{实现部分}

#include<bits/stdc++.h>
#define reg register

int read(){
        char c;
        int s = 0, flag = 1;
        while((c=getchar()) && !isdigit(c))
                if(c == '-'){ flag = -1, c = getchar(); break ; }
        while(isdigit(c)) s = s*10 + c-'0', c = getchar();
        return s * flag;
}

const int maxn = 2e5 + 10;
const int inf = 0x3f3f3f3f;

int N;
int M;
int len;
int tot;
int A[maxn];
int cf[maxn];
int nxt[maxn];

char S[maxn];
char T[maxn];

struct Intval{ int l, r; } B[maxn*10];

bool cmp(Intval a, Intval b){ return a.r < b.r; }

void get_next(){
        nxt[0] = -1;
        int i = 0, t = -1;
        while(i < len)
                if(t == -1 || T[i] == T[t]) nxt[++ i] = ++ t;
                else t = nxt[t];
}

void Fuck(){
        int t1 = 0, t2 = 0;
        while(t1 < N){
                if(t2 == -1 || S[t1] == T[t2]) t1 ++, t2 ++;
                else t2 = nxt[t2];
                if(t2 == len){
//                        cf[t1-len+1] ++, cf[t1-1+1+1] --;
                        B[++ tot].l = t1-len+1, B[tot].r = t1;
                        t2 = nxt[t2];
                }
        }
}

struct Segment_Tree{

        struct Node{ int l, r, v; } T[maxn << 2];

        void Build(int k, int l, int r){
                T[k].l = l, T[k].r = r, T[k].v = inf;
                if(l == r) return ;
                int mid = l+r >> 1;
                Build(k<<1, l, mid), Build(k<<1|1, mid+1, r);
        }

        void Add(int k, const int &aim, const int &x){
                int l = T[k].l, r = T[k].r;
                if(l == r) return T[k].v = x, void();
                int mid = l+r >> 1;
                if(aim <= mid) Add(k<<1, aim, x);
                else Add(k<<1|1, aim, x);
                T[k].v = std::min(T[k<<1].v, T[k<<1|1].v);
        }

        int Query(int k, const int &ql, const int &qr){
                int l = T[k].l, r = T[k].r;
                if(ql <= l && r <= qr) return T[k].v;
                int mid = l+r >> 1, res = inf;
                if(ql <= mid) res = Query(k<<1, ql, qr);
                if(qr > mid) res = std::min(res, Query(k<<1|1, ql, qr));
                return res;
        }

} seg_t;

int main(){
//        freopen("magic.in", "r", stdin);
//        freopen("magic.out", "w", stdout);
        N = read(), M = read();
        scanf("%s", S);
        for(reg int i = 1; i <= N; i ++) A[i] = read();
        for(reg int i = 1; i <= M; i ++){
                scanf("%s", T);
                len = strlen(T);
                get_next(); Fuck();
        }
//        for(reg int i = 1; i <= N; i ++) cf[i] += cf[i-1];
        std::sort(B+1, B+tot+1, cmp);
        int t = 1, l = 0; seg_t.Build(1, 1, N);
        for(reg int i = 1; i <= N; i ++){
                if(!l) seg_t.Add(1, i, A[i]);
                else seg_t.Add(1, i, A[i]+seg_t.Query(1, l, i-1));
                while(t <= tot && B[t].r == i) l = std::max(l, B[t ++].l);
        }
        printf("%d\n", seg_t.Query(1, l, N));
        return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值