【巧妙转化+DP】AGC036D

题目大意:

给你一个\(n\)个点的有向完全图。对于边\(u\rightarrow v\),如果\(u<v\)\(u\neq v-1\),边权为\(-1\)。如果\(u>v\)边权为\(1\),否则边权为\(0\)

现在你要删除一些边权为\(-1\)\(1\)的边,使\(1\rightarrow n\)有最短路。删除边\(u \rightarrow v\)\(w_{u\rightarrow v}\)的花费,求满足要求的最小花费

\(n\leq 500\)

一个图没有负环等价于它有最短路,而最短路是满足三角不等式的,即$dis_v\geq dis_u+w_{u\rightarrow v} $

因为有\(u\rightarrow u+1\)的零边存在,所以有\(dis_{u+1}\geq dis_u\)。因为有\(u<v\)时边权为\(-1\),所以\(dis_v\geq dis_u-1\;(u<v)\),整理一下有\(0 \leq dis_{u}-dis_{v}\leq 1\)

记差分数组\(c_i=dis_i-dis_{i+1}\),根据上面有\(0\leq c_i \leq 1\)

考虑边的限制:当\(u>v\)时,有\(\sum\limits_{i=v}^{u-1} c_i\leq 1\),当\(u<v\)时,有\(\sum\limits_{i=u}^{v-1} c_i\geq 1\)

即:\([u,v]\)中最多只有一个\(1\)\([u,v]\)中至少要有一个\(1\)

于是问题就转化成了这样:给你一个\(n\)个数的序列,每个元素可以填\(0\)\(1\)。对于一种填法,要删除一些限制,使这种填法满足剩下所有限制。

于是可以欢乐dp了

扫描二维码关注公众号,回复: 7755958 查看本文章

\(f[i][j]\)表示dp到第\(i\)位,上一个\(1\)在第\(j\)位的最小花费

转移时枚举\(k\),然后强制第\(i\)位为\(1\),于是有\(f[k][i]\leftarrow f[i][j]\),转移是分类讨论一下\(u>v\)\(u<v\)的限制要被去掉的区间,这个可以二维前缀和算。

代码:

#include <bits/stdc++.h>
#define N 510
#define ll long long
#define For(i,x,y) for(int i=(x);i<=(y);++i)
#define Rof(i,x,y) for(int i=(x);i>=(y);--i)
#define Edge(x) for(int i=head[x];i;i=e[i].nxt)
#define mset(x,y) memset(x,y,sizeof(x))
using namespace std;
ll dp[N][N],A[N][N],B[N][N],a[N][N],b[N][N];
ll getB(int rx,int ry,int lx,int ly){
    if(lx>rx||ly>ry) return 0;
    return B[rx][ry]-B[lx-1][ry]-B[rx][ly-1]+B[lx-1][ly-1];
}
ll getA(int rx,int ry,int lx,int ly){
    if(lx>rx||ly>ry) return 0;
    return A[rx][ry]-A[lx-1][ry]-A[rx][ly-1]+A[lx-1][ly-1];
}
void chkmin(ll &x,ll y){ if(y<x) x=y; }
int main(){
    int n;ll ans=0;
    scanf("%d",&n);
    For(i,1,n){
        For(j,1,i-1) scanf("%lld",&b[i][j]);
        For(j,i+1,n) scanf("%lld",&a[i][j]);
        For(j,1,n){
            A[i][j]=A[i-1][j]+A[i][j-1]+a[i][j]-A[i-1][j-1];
            B[i][j]=B[i-1][j]+B[i][j-1]+b[i][j]-B[i-1][j-1];
        }
    }
    mset(dp,0x3f);ans=dp[0][0];
    dp[0][0]=0;
    For(i,0,n) For(j,0,max(i-1,0)) For(k,i+1,n)
        chkmin(dp[k][i],dp[i][j]+getB(n,i,k+1,j+1)+getA(k,k,i+1,i+1));
    For(j,1,n) chkmin(ans,dp[n][j]);
    printf("%lld",ans);
}

猜你喜欢

转载自www.cnblogs.com/PsychicBoom/p/11800989.html