题意
有一个
n
n
n个点的有向图,从
i
i
i到
i
+
1
i+1
i+1有一条权值为
0
0
0的边,对于所有满足
i
<
j
i<j
i<j的数对
(
i
,
j
)
(i,j)
(i,j),从
i
i
i到
j
j
j有一条权值为
−
1
-1
−1的边,
j
j
j到
i
i
i有权值为
1
1
1的边。删掉每条边都有一个代价,且权值为
0
0
0的边不能删。求最小的代价使得图中不存在负环。
n
≤
500
n\le500
n≤500
分析
图中不存在负环的充要条件是存在一个数组
d
i
d_i
di,使得若存在一条
i
i
i到
j
j
j权值为
w
w
w的边,则必有
d
i
+
w
≥
d
j
d_i+w\ge d_j
di+w≥dj。
不妨把从
1
1
1号点开始求单源最短路得到的最短路数组作为数组
d
d
d。
显然若
i
<
j
i<j
i<j,则
d
i
≥
d
j
d_i\ge d_j
di≥dj,我们设
p
i
=
d
i
−
d
i
+
1
p_i=d_i-d_{i+1}
pi=di−di+1,有
p
i
≥
0
p_i\ge 0
pi≥0。
对于一条
i
i
i到
j
j
j边权为
−
1
-1
−1的边,可以得到
d
i
−
1
≥
d
j
d_i-1\ge d_j
di−1≥dj也就是
p
i
+
.
.
.
+
p
j
−
1
≥
1
p_i+...+p_{j-1}\ge 1
pi+...+pj−1≥1
对于
j
j
j到
i
i
i边权为
1
1
1的边同理可得
p
i
+
.
.
.
+
p
j
−
1
≤
1
p_i+...+p_{j-1}\le 1
pi+...+pj−1≤1。
也就是说假设
p
p
p数组固定了,剩下的只要把不满足的边删掉即可。
显然最优情况下
p
i
p_i
pi只会取
0
0
0或
1
1
1,设
d
p
i
,
j
dp_{i,j}
dpi,j表示前
j
j
j个位置已经固定,
p
i
=
p
j
=
1
p_i=p_j=1
pi=pj=1,且
p
i
p_i
pi和
p
j
p_j
pj中间全为
0
0
0花费的最小代价。
预处理前缀和,然后枚举下一个
1
1
1进行转移即可。
时间复杂度
O
(
n
3
)
O(n^3)
O(n3)。
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
typedef long long LL;
const int N=505;
const LL inf=(LL)1e17;
int n;
LL s1[N][N],s2[N][N],a[N][N],f[N][N];
void pre()
{
for (int l=1;l<n;l++)
for (int i=1;i+l<=n;i++)
{
int j=i+l;
s1[i][j]=s1[i][j-1]+s1[i+1][j]-s1[i+1][j-1]+a[i][j];
}
for (int i=1;i<=n;i++)
for (int j=n;j>i;j--)
s2[i][j]=s2[i][j+1]+a[j][i];
for (int j=1;j<=n;j++)
for (int i=1;i<j;i++)
s2[i][j]+=s2[i-1][j];
}
LL dp()
{
for (int i=0;i<=n;i++)
for (int j=0;j<=n;j++)
f[i][j]=inf;
LL ans=s1[1][n];
for (int i=1;i<=n;i++) ans=std::min(ans,s1[1][i]+s1[i+1][n]);
for (int i=1;i<n;i++)
for (int j=i+1;j<=n;j++)
f[i][j]=s1[1][i]+s1[i+1][j]+s2[i][j+1];
for (int j=1;j<=n;j++)
for (int i=1;i<j;i++)
{
ans=std::min(ans,f[i][j]+s1[j+1][n]);
for (int k=j+1;k<=n;k++)
f[j][k]=std::min(f[j][k],f[i][j]+s1[j+1][k]+s2[j][k+1]-s2[i][k+1]);
}
return ans;
}
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
if (i!=j) scanf("%lld",&a[i][j]);
pre();
printf("%lld\n",dp());
return 0;
}