潜龙未见静水流,沉默深藏待时秋。一朝破空声势振,惊世骇俗展雄猷。
随笔 - 80, 文章 - 0, 评论 - 2, 阅读 - 1991

决策单调性学习笔记

目录

一、四边形不等式
定义
1D1D 中的决策单调性
2D1D 中的决策单调性
二、二分队列写法
三、分治写法
四、区间 DP 写法
五、相关例题
例1、P1912 [NOI2009] 诗人小G
例2、LOJ6039 「雅礼集训 2017 Day5」珠宝
例3、CF868F Yet Another Minimization Problem
例4、CF1603D Artistic Partition
例5、P5892 [IOI2014]holiday 假期
例6、P4767 [IOI2000]邮局

一、四边形不等式

定义

如果二元函数 w 满足,abcd ,均有 w(a,d)+w(b,c)w(a,c)+w(b,d) ,则称 w 满足四边形不等式。

结论:二元函数 w 满足四边形不等式的充要条件为, a<b ,均有 w(a,b+1)+w(a+1,b)w(a,b)+w(a+1,b+1)

证明:

充分性显然,下面考虑必要性。

条件等价于,对于任意一个 2×2 的正方形,左上加右下右上加左下。

目标等价于,对于任意两行两列的子矩阵,左上加右下 右上加左下。

将以 (a,c),,(a,d) 为左上角的正方形的式子相加,可得,对于所有行以及 c,d 两列构成的子矩阵,它满足四边形不等式。

在行的维度上做相同的操作即可得证。

1D1D 中的决策单调性

对于形如 fi=min0j<i(fj+wj,i) 的转移方程,如果最优决策点 pi (非严格)单调递增,则称 f 满足决策单调性。

注:如果有多个决策点同时达到最优,我们需要人为规定取最左或最右的决策点,但不能任取一个作为最优决策点。

结论:如果 w 满足四边形不等式,则 f 满足决策单调性。

证明:

反证法,假设存在 i<j,pi>pj ,根据最优性:

fpj+wpj,ifpi+wpi,ifpi+wpi,jfpj+wpj,i

相加可得 wpj,i+wpi,jwpi,i+wpj,j

注意到 pi<i ,但这与四边形不等式矛盾。

如果 f 的转移是 max 形式,那么四边形不等式需要反号。总而言之,四边形不等式可以简记为 "交叉优于包含" 。

划重点:如果仅有最优决策满足决策单调性,那么并不能使用下面 "二分队列" 的方法进行优化;我们需要保证对于任意两个决策点,一个前缀更优,一个后缀更优,才可以使用 "二分队列" 进行优化。

2D1D 中的决策单调性

对于形如 fl,r=minlk<r(fl,k+fk+1,r)+wl,r 的转移方程,如果最优决策点 pl,r 满足pl,r1pl,rpl+1,r,则称 f 满足决策单调性。

结论:如果 w 满足下述条件,则 f 满足四边形不等式和决策单调性。

  • w 满足四边形不等式。
  • i,wi,i=0
  • abcd,wa,dwb,c

证明过于繁琐,懒得写了。甩个链接,想看的自己去看。


由于某些显而易见的原因,本文例题部分不会严谨证明转移矩阵满足四边形不等式。

在实际操作中,如果你认为某个 dp 方程可以用决策单调性优化,不妨通过打表判断决策点是否单调来验证你的猜想。

二、二分队列写法

适用范围:绝大多数 1D1D 决策单调性优化。

转移方程:fi=min0j<i(fj+wj,i),其中 wj,i 满足四边形不等式。

二分队列的主要思想是对每个 i ,维护其最优决策 pi 的值。

每计算出一个 fi ,它可以作为决策点去更新后面的 f 值。根据决策单调性,i 仅可能作为一个后缀的最优决策(注意当前已有的决策点只有 1i )。

具体实现时,用一个单调队列维护三元组 {x,l,r} ,表示决策点 x 是区间 [l,r] 的最优决策点。

在插入一个点 i 时,倒序枚举每个三元组,如果对于左端点 l ,满足最优决策点不再是原来的决策点 x (而变为当前点 i ),根据决策单调性,整个区间的最优决策点都会变成 i ,从而可以把这个区间删去。

如果对于左端点 l ,最优决策点并没有发生变化,由于需要变成i的位置是一段后缀,在区间内二分即可。

时间复杂度O(nlogn)

struct node
{
    int x,l,r;
}q[maxn];///单调队列
int calc(int j,int i)
{///j向i的转移代价,保证j<i
    return f[j]+w(j,i);
}
int main()
{
    q[h=t=1]={0,1,n};///初始所有位置的最优决策点均为0
    for(int i=1;i<=n;i++)
    {
        while(h<t&&q[h].r<i) h++;///删掉已经过时的决策点,注意一定不会删空
        f[i]=calc(q[h].x,i);///更新f[i]的值
        while(h<t&&calc(i,q[t].l)<=calc(q[t].x,q[t].l)) t--;///如果从i转移比从原先的决策点优,删掉这个区间
        int l=max(i,q[t].l-1),r=q[t].r+1;///二分有效区间(l,r],如果i对整个区间都不优返回q[t].r+1
        ///注意calc函数在j>=i时可能无定义,所以需要保证l>=i
        while(r-l>1)
        {
            int mid=(l+r)/2;
            if(calc(i,mid)<=calc(q[t].x,mid)) r=mid;
            else l=mid;
        }
        if(r<=n) q[t].r=r-1,q[++t]={i,r,n};///更新队尾
    }
}

注意第16pop时并没有把队列弹空,而是留下最后一个元素用于二分

Bouns :对于另一类满足 i<j,pipj 的决策单调性,可以用 "二分栈" 实现,具体可见例题柠檬

三、分治写法

适用范围: ij 的转移不依赖于 fi ,常见于分层的 1D1D 转移。

转移方程: fi=min1jn(gj+wj,i)

向下递归分治,假设当前正在处理 flfr 的值,已知 plL,prR

mid=l+r2 ,根据决策单调性, LpmidR

先暴力扫描区间 [L,R] ,求出 fmidpmid 的值,再分别递归两侧即可。

每向下递归一层区间 [l,r] 长度会减半,因此至多只会递归 O(logn) 层。

每层 (rl)=(RL)=O(n) ,时间复杂度为O(nlogn)

int calc(int j,int i)
{///上一层j向当前层i的转移代价,不一定需要j<i
    return g[j]+w(j,i);
}
void solve(int l,int r,int L,int R)
{///正在计算f[l~r],保证最优决策点包含[L,R]
    if(l>r) return ;
    int mid=(l+r)/2,pos=0,val=inf;
    for(int i=L;i<=R;i++)
        if(calc(mid,i)<val)
            pos=i,val=calc(mid,i);
    f[mid]=val;///暴力求出f[mid]的值,最优决策点为pos
    solve(l,mid-1,L,pos);
    solve(mid+1,r,pos,R);
}

如果保证 pi<i ,也可以用 "二分队列" 实现。

如果 jicalc 函数无定义,需要返回 inf 。当分治递归到 r<=i 的区间时 f[mid]=inf,pos=0 ,但分治可以正常进行,无需特殊处理


小技巧:如果 w(l,r) 不能 O(1) 计算,但是可以 O(1) 从 w(l±1,r) 和 w(l,r±1) 递推得到,我们可以用类似莫队的方式移动指针处理。

证明:

对于右端点,单次移动次数和 rl 同阶。

对于左端点,单次移动次数和 RL 同阶。

时间复杂度 O((rl+RL)logn)=O(nlogn)

四、区间 DP 写法

适用范围:一般用于优化 2D1DDP

转移方程:fl,r=minlk<r(fl,k+fk+1,r+wl,r)

记录最优决策点 pl,r ,暴力枚举 [pl,r1,pl+1,r] 范围内的所有决策点即可。

时间复杂度 O(n2) ,证明如下:

计算 fl,r 的答案时,枚举量为 pl+1,rpl,r1

放在二维矩阵上看,相当于 fl,r 给下边的格子贡献系数 1 ,给左边的格子贡献系数 1 ,总代价为所有格子的系数乘以 pl,r 之和。

image

如上图,灰色是不合法的格子,有且仅有红色格子系数为 +1 ,绿色格子系数为 -1

时间复杂度为 O(pi,np1,i)=O(n2)

代码非常好写:

for(int i=1;i<=n;i++) f[i][i]=w(i,i),p[i][i]=i;
for(int len=2;len<=n;len++)///区间长度从2开始枚举
    for(int l=1,r=len;r<=n;l++,r++)
    {
        f[l][r]=inf;
        for(int k=p[l][r-1];k<=p[l+1][r];k++)
            if(f[l][k]+f[k+1][r]+w(l,r)<=f[l][r])
                f[l][r]=f[l][k]+f[k+1][r]+w(l,r),p[l][r]=k;
    }

五、相关例题

例1、P1912 [NOI2009] 诗人小G

题目描述

T 组数据,给定 n 个句子,行标准长度为 l 。每一行可以放若干个连续的句子,相邻两个句子之间需要一个空格。

每一行的不协调度为该行实际长度与 l 之差的绝对值的 p 次方,求所有行的不协调度的最小值。

数据范围

  • 1T10,1n105,1l3106,1p10
  • 句子长度 30

时间限制 2s ,空间限制 250MB

分析

记句子长度前缀和为 si ,写下前 i 个句子的最小不协调度为 fi ,转移方程为:

fi=min0j<i(fj+|sisj+ij1l|p)

打表可知wj,i=|sisj+ij1|p满足四边形不等式,用二分栈实现。

注意本题答案会爆 long long ,所以需要用 long double 存储 dp 数组,通过牺牲精度的方式换取更大的值域。

时间复杂度 O(Tnlogn)

#include<bits/stdc++.h>
#define ld long double
using namespace std;
const int maxn=1e5+5;
int h,l,n,p,t,cas;
char ch[maxn][35];
int s[maxn],pos[maxn];
ld f[maxn];
struct node
{
    int x,l,r;
}q[maxn];
ld qpow(ld a,int k)
{
    ld res=1;
    for(int i=1;i<=k;i++) res*=a;
    return res;
}
ld calc(int j,int i)
{
    return f[j]+qpow(abs(s[i]-s[j]+i-j-1-l),p);
}
void print(int n)
{
    if(!n) return ;
    print(pos[n]);
    for(int i=pos[n]+1;i<=n;i++) printf("%s%c",ch[i]+1,i!=n?' ':'\n');
}
int main()
{
    scanf("%d",&cas);
    while(cas--)
    {
        scanf("%d%d%d",&n,&l,&p);
        for(int i=1;i<=n;i++)
        {
            scanf("%s",ch[i]+1);
            s[i]=s[i-1]+strlen(ch[i]+1);
        }
        q[h=t=1]={0,1,n};
        for(int i=1;i<=n;i++)
        {
            while(h<t&&q[h].r<i) h++;
            f[i]=calc(q[h].x,i),pos[i]=q[h].x;///记录最优决策点,用于输出方案
            while(h<t&&calc(i,q[t].l)<calc(q[t].x,q[t].l)) t--;
            int l=q[t].l-1,r=q[t].r+1;
            while(r-l>1)
            {
                int mid=(l+r)/2;
                if(calc(i,mid)<calc(q[t].x,mid)) r=mid;
                else l=mid;
            }
            if(r<=n) q[t].r=r-1,q[++t]={i,r,n};
        }
        if(f[n]<=1e18) printf("%.0Lf\n",f[n]),print(n);
        else printf("Too hard to arrange\n");
        printf("--------------------\n");
    }
    return 0;
}

例2、LOJ6039 「雅礼集训 2017 Day5」珠宝

题目描述

n 种珠宝,第 i 种的价格为 ci 元,吸引力为 vi

1ik ,假如你可以支付不超过 i 元,求吸引力之和的最大值。

数据范围

  • 1n106,1k5104,1ci300,0vi109

时间限制 2s ,空间限制 256MB

分析

普通背包的时间复杂度为 O(nk) ,肯定过不了。

但是单个物品的价格很小,这启示我们重新设计状态。

将所有价格相同的物品按吸引力降序排序,显然只会取一个前缀,记前缀和为 sk

fi,j 表示考虑所有价格 i 的物品,总价格为 j ,吸引力之和的最大值。转移方程为:

fi,j=max0ckj(fi,jck+sk)

下标按照 modc 分类,转移矩阵 w(i,j)=sji ,满足决策单调性。

关于本题 w(i,j) 的决策单调性的感性理解: "交叉" 和 "包含" 选择的物品个数是相同的,但 "交叉" 取的物品吸引力更大,所以更优。

对每个 modc 的剩余系分别用决策单调性优化即可。

时间复杂度 O(300klogk)

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=5e4+5;
int c,k,n,v;
ll f[maxn],g[maxn],s[maxn],dp[maxn];
vector<int> vec[maxn];
void solve(int l,int r,int L,int R)
{
    if(l>r) return ;
    int mid=(l+r)/2,pos=0;
    ll val=-1e18;
    for(int i=L;i<=R&&i<=mid;i++)///i>mid时不能转移,可以认为w(i,mid)=-inf
        if(g[i]+s[mid-i]>val)
            val=g[i]+s[mid-i],pos=i;
    f[mid]=val;
    solve(l,mid-1,L,pos);
    solve(mid+1,r,pos,R);
}
int main()
{
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++) scanf("%d%d",&c,&v),vec[c].push_back(v);
    for(int i=1;i<=300;i++)
    {
        if(vec[i].empty()) continue;
        sort(vec[i].begin(),vec[i].end(),greater<int>());
        int l=vec[i].size();
        for(int j=1;j<=k/i;j++) s[j]=j<=l?s[j-1]+vec[i][j-1]:0;
        for(int r=0;r<i;r++)
        {
            int m=0;
            for(int j=r;j<=k;j+=i) g[++m]=dp[j];
            solve(1,m,1,m);
            for(int j=r;j<=k;j+=i) dp[j]=f[j/i+1];
        }
    }
    for(int i=1;i<=k;i++) printf("%lld ",dp[i]);
    putchar('\n');
    return 0;
}

例3、CF868F Yet Another Minimization Problem

同类题P5574 [CmdOI2019]任务分配问题

题目描述

给定长为 n 的序列 a ,要求分成 k 段,每段代价为 li<jr[ai=aj]

求所有划分方案中,代价之和的最小值。

数据范围

  • 2n105,2kmin(n,20),1ain

时间限制 2s ,空间限制 256MB

分析

fi,j 表示将 [1,j] 划分为 i 段,代价之和的最小值。转移方程为:

fi,j=min0k<j(fi1,k+wk+1,j)

其中 w(l,r) 为区间 [l,r] 中相等元素的对数,满足四边形不等式。

注意 w 不能 O(1) 计算,需要用莫队维护指针的方式处理。

时间复杂度 O(knlogn)

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=1e5+5,inf=1e18;
int k,l=1,n,r,sum;
int a[maxn],cnt[maxn];
int f[25][maxn];
void add(int x)
{
    sum+=cnt[a[x]]++;
}
void del(int x)
{
    sum-=--cnt[a[x]];
}
int calc(int L,int R)
{
    while(l>L) add(--l);
    while(r<R) add(++r);
    while(l<L) del(l++);
    while(r>R) del(r--);
    return sum;
}
void solve(int i,int l,int r,int L,int R)
{
    if(l>r) return ;
    int mid=(l+r)/2,pos=0,val=inf;
    for(int j=L;j<=R&&j<mid;j++)
        if(f[i-1][j]+calc(j+1,mid)<=val)
            val=f[i-1][j]+calc(j+1,mid),pos=j;
    f[i][mid]=val;
    solve(i,l,mid-1,L,pos);
    solve(i,mid+1,r,pos,R);
}
signed main()
{
    scanf("%lld%lld",&n,&k);
    for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
    for(int i=0;i<=k;i++) for(int j=0;j<=n;j++) f[i][j]=inf;
    f[0][0]=0;
    for(int i=1;i<=k;i++) solve(i,0,n,0,n);
    printf("%lld\n",f[k][n]);
    return 0;
}

注意执行到第 31 行时 pos 可能为零,这意味着 f[i][mid] 是一个不合法的状态( mid<i )。

此时不需要手动 return ,因为区间内其他f值可能还没被算到,但是更稳妥的方法是在第 27 行赋初始值 pos=L (或 [L,R] 内任何一个数)。

例4、CF1603D Artistic Partition

题目描述

定义 c(l,r)=lijr[gcd(i,j)l]

T 组数据,给定 n,k ,求min0=x1<<xk+1=ni=1kc(xi,xi+1)

数据范围

  • 1T3105,1kn105

时间限制 3s ,空间限制 1024MB

分析

容易想到动态规划, fi,j 表示将 [1,j] 划分成 i 段,代价之和的最小值。

乍一看状态数 O(n2) ,但是注意到 c(l,r)rl+1 ,并且 c(l,l)=1 ,因此n2k1 时,答案一定为 n

于是第一维上界变成 logn ,总状态数 O(nlogn) 可以接受。

转移方程如下:

fi,j=min0k<j(fi1,k+c(k+1,j))

先把 c(l,r) 化简一下:

lijr[gcd(i,j)l]=d=lrlijr[gcd(i,j)=d]=d=lr1ijrd[gcd(i,j)=1]=d=lrj=1rdφ(j)

预处理 φ 的前缀和,单次计算c可以用整除分块优化到 O(rl)

发现 c 满足四边形不等式。在分治暴力寻找决策点时,由于r固定,所以可以从 c(l,r) 直接递推得到 c(l1,r) 的值。

求出整个 dp 数组后可以 O(1) 回答询问,时间复杂度 O(nlog2n+T)

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=1e5+5,inf=1e18;
int k,n,t;
int b[maxn],p[maxn],s[maxn],phi[maxn];
int f[18][maxn];
int calc(int x,int y)
{
    int res=0;
    for(int l=x,r=0;l<=y;l=r+1) r=y/(y/l),res+=(r-l+1)*s[y/l];
    return res;
}
void solve(int i,int l,int r,int L,int R)
{
    if(l>r) return ;
    int mid=(l+r)/2,pos=0,val=inf;
    for(int j=min(mid,R),now=calc(j+1,mid);j>=L;j--)
    {
        if(f[i-1][j]+now<val) val=f[i-1][j]+now,pos=j;
        now+=s[mid/j];
    }
    f[i][mid]=val;
    solve(i,l,mid-1,L,pos);
    solve(i,mid+1,r,pos,R);
}
void init(int n)
{
    phi[1]=1;
    for(int i=2,cnt=0;i<=n;i++)
    {
        if(!b[i]) p[++cnt]=i,phi[i]=i-1;
        for(int j=1;j<=cnt&&i*p[j]<=n;j++)
        {
            b[i*p[j]]=1;
            if(i%p[j]==0)
            {
                phi[i*p[j]]=phi[i]*p[j];
                break;
            }
            phi[i*p[j]]=phi[i]*phi[p[j]];
        }
    }
    for(int i=1;i<=n;i++) s[i]=s[i-1]+phi[i];
    for(int i=1;i<=n;i++) f[1][i]=i*(i+1)/2;
    for(int i=2;i<=17;i++) solve(i,1,n,1,n);
}
signed main()
{
    scanf("%lld",&t),init(maxn-5);
    while(t--)
    {
        scanf("%lld%lld",&n,&k);
        printf("%lld\n",k>=18?n:f[k][n]);
    }
    return 0;
}

例5、P5892 [IOI2014]holiday 假期

题目描述

数轴上有 n 个点,第 i 个点的权值为 ai

给定起点 s ,每次操作要么移动到相邻点,要么停留在第 i 个点并获得 ai 的收益,每个点至多停留一次。

d 次操作后收益的最大值。

数据范围

  • 2n105,0d52n,0ai109

时间限制 1s ,空间限制 64MB

分析

假设我们已经确定了经过的区间 [l,r] ,显然我们只会选择区间内最大的 d(rl+1)min(sl,rs) 个点。

用可持久化线段树维护,可以做到单次 O(logn) 计算一个区间 [l,r] 的答案。

对固定的 l ,记最优的决策点 rpl ,容易发现 pl 单调递增。

分治优化,时间复杂度 O(nlog2n)

本题 O(nlogV) 的空间是过不去的,需要对 ai 离散化。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e5+5,inf=1e9;
int d,n,s,cnt,tot;
ll res;
int a[maxn],c[maxn],rt[maxn];
struct node
{
    int ls,rs;
    ll cnt,sum;
}f[20*maxn];
void pushup(int p)
{
    f[p].cnt=f[f[p].ls].cnt+f[f[p].rs].cnt;
    f[p].sum=f[f[p].ls].sum+f[f[p].rs].sum;
}
int insert(int p,int l,int r,int pos)
{
    int q=++tot;
    f[q]=f[p];
    if(l==r)
    {
        f[q].cnt++,f[q].sum+=c[pos];
        return q;
    }
    int mid=(l+r)/2;
    if(pos<=mid) f[q].ls=insert(f[q].ls,l,mid,pos);
    else f[q].rs=insert(f[q].rs,mid+1,r,pos);
    return pushup(q),q;
}
ll query(int p,int q,int l,int r,int k)
{
    if(l==r) return 1ll*c[l]*k;
    int mid=(l+r)/2,cur=f[f[q].rs].cnt-f[f[p].rs].cnt;
    if(k<=cur) return query(f[p].rs,f[q].rs,mid+1,r,k);
    else return f[f[q].rs].sum-f[f[p].rs].sum+query(f[p].ls,f[q].ls,l,mid,k-cur);
}
ll calc(int l,int r)
{
    int w=d-(r-l)-min(s-l,r-s);
    return w<=0?0:query(rt[l-1],rt[r],1,cnt,min(w,r-l+1));
}
void solve(int l,int r,int L,int R)
{
    if(l>r) return ;
    int mid=(l+r)/2,pos=0;
    ll val=-inf;
    for(int i=L;i<=R;i++)
    {
        ll now=calc(mid,i);
        if(now>val) val=now,pos=i;
    }
    res=max(res,val);
    solve(l,mid-1,L,pos);
    solve(mid+1,r,pos,R);
}
int main()
{
    scanf("%d%d%d",&n,&s,&d),s++;
    for(int i=1;i<=n;i++) scanf("%d",&a[i]),c[i]=a[i];
    sort(c+1,c+n+1);
    cnt=unique(c+1,c+n+1)-c-1;
    for(int i=1;i<=n;i++)
    {
        a[i]=lower_bound(c+1,c+cnt+1,a[i])-c;
        rt[i]=insert(rt[i-1],1,cnt,a[i]);
    }
    solve(1,s,s,n);
    printf("%lld\n",res);
    return 0;
}

例6、P4767 [IOI2000]邮局

题目描述

数轴上有 n 个点 a1<<an ,你需要在数轴上选 k 个点 x1,,xk ,使得 i=1nmin1jk|aixj| 最小。

数据范围

  • 1n3000,1kmin(n,300),1ai104

时间限制 1s ,空间限制 125MB

分析

显然每个 xj 会作用于一段区间,反过来,对于一个给定的区间 [l,r] ,将 xj 放在中位数位置一定最优。

fi,j 表示用 j 个点覆盖 a1ai 的最小代价。转移方程为:

fi,j=min0k<j1fk,j1+wk+1,j

其中 wl,r 表示选择一个 xj 覆盖区间 [l,r] 的代价,可以 O(n2) 递推,也可以求前缀和后单次 O(1) 回答。

容易发现 w 满足四边形不等式,用区间 DP 写法优化即可。

如果把第二维放在前面,也可以用二分队列或者分治进行优化,但时间复杂度会多一只 log

边界 fi,i=0 ,由于计算 fi,j 需要用到 pi,j1pi+1,j 的值,所以需要先升序枚举 j ,再倒序枚举 i

时间复杂度 O(kn)

#include<bits/stdc++.h>
using namespace std;
const int maxn=3005;
int k,n;
int a[maxn],w[maxn][maxn];
int f[maxn][305],p[maxn][305];
int main()
{
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    for(int len=1;len<=n;len++)
        for(int l=1,r=len;l<=n;l++,r++)
            w[l][r]=w[l+1][r-1]+a[r]-a[l];
    memset(f,0x3f,sizeof(f));
    f[0][0]=0;
    for(int j=1;j<=k;j++)
    {
        p[n+1][j]=n;
        for(int i=n;i>=1;i--)
        {
            for(int l=p[i][j-1];l<=p[i+1][j];l++)
                if(f[l][j-1]+w[l+1][i]<=f[i][j])
                    f[i][j]=f[l][j-1]+w[l+1][i],p[i][j]=l;
        }
    }
    printf("%d\n",f[n][k]);
    return 0;
}

posted on   peiwenjun  阅读(8)  评论(0编辑  收藏  举报

相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具

导航

< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5
点击右上角即可分享
微信分享提示