正题
题目链接:https://www.luogu.com.cn/problem/AT5147
题目大意
有nnn个点的一张图,其中i→i+1(i<n)i\rightarrow i+1(i< n)i→i+1(i<n)有一条边权值为000。
对于其他i,j(i≠j)i,j(i\neq j)i,j(i=j)存在一条边i→ji\rightarrow ji→j,若i<ji<ji<j那么权值为−1-1−1,否则为111。
删除i→j(i≠j)i\rightarrow j(i\neq j)i→j(i=j)的代价为ai,ja_{i,j}ai,j,要求代价最小的情况下使得图中不存在负环。
1≤n≤5001\leq n\leq 5001≤n≤500
解题思路
这个容易负环让人一头雾水不知道怎么维护,我们知道差分约束有解的条件就是没有负环,所以我们可以考虑转成差分约束模型。
那么对于不能删除的边i→i+1i\rightarrow i+1i→i+1就有限制xi≥xi+1x_i\geq x_{i+1}xi≥xi+1,也就是整个序列单调不升。
然后形如i→j(i<j)i\rightarrow j(i<j)i→j(i<j)可以视为xi−1≥xjx_i-1\geq x_jxi−1≥xj。
形如i←j(i<j)i\leftarrow j(i<j)i←j(i<j)可以视为xi≤xj+1x_i\leq x_j+1xi≤xj+1。
也就是xi−xj≤1x_i-x_j\leq 1xi−xj≤1和xi−xj≥1x_i-x_j\geq 1xi−xj≥1的限制,我们考虑维护差分数组(反过来的)yi=xi−1−xiy_i=x_{i-1}-x_{i}yi=xi−1−xi,那么条件就是区间和不能大于/小于111,显然的那么yiy_iyi就只有可能是0/10/10/1。
然后仔细观察这个限制会发现其实只有相邻的111会有用,我们考虑dpdpdp来处理,设fi,j,kf_{i,j,k}fi,j,k表示做到第iii个,上一个111在jjj处,再上一个111在kkk处。
前缀和一下aaa数组优化转移即可。
时间复杂度:O(n3)O(n^3)O(n3)
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=510;
ll n,a[N][N],s[N][N],t[N][N],f[N][N],ans;
signed main()
{scanf("%lld",&n);for(ll i=1;i<=n;i++)for(ll j=1;j<=n;j++){if(i==j)continue;scanf("%lld",&a[i][j]);}for(ll i=1;i<=n;i++)for(ll j=1;j<=i;j++)s[i][j]=s[i][j-1]+a[j][i],t[i][j]=t[i][j-1]+a[i][j];memset(f,0x3f,sizeof(f));ans=f[0][0];f[1][1]=0;for(ll i=2;i<=n;i++){for(ll j=1;j<i;j++)for(ll k=1;k<=j-(j!=1);k++)f[i][j]=min(f[i][j],f[j][k]);for(ll j=1;j<=i;j++)for(ll k=1;k<=j-(j!=1);k++)f[j][k]+=s[i][i]-s[i][j-1],f[j][k]+=t[i][k-1];}for(ll i=1;i<=n;i++)for(ll j=1;j<=n;j++)ans=min(ans,f[i][j]);printf("%lld\n",ans);return 0;
}