AtCoder agc036_d Negative Cycle
发现图中有没有负环的判定是困难的(也不能说难,但是判定的话很慢)。考虑有什么更好的方法,发现图中没有负环等价于差分约束有解。
现在转化一下题目中的条件:
-
\(x_i\ge x_{i+1}\)。
-
\(i<j\) 时,\(x_i-1\ge x_j\)。
-
\(i>j\) 时,\(x_i+1\ge x_j\)。
然后发现,若我们保留 \(i<j\) 的 \(i\rightarrow j\) 边,则 \(i<k<j\) 的 \(i\rightarrow k\) 边。\(i>j\) 时同理。
然后发现,对于一个位置 \(i\),他向后的连边当且仅当 \(x_i=x_j\) 需要断开,向前的连边当且仅当 \(x_j\ge x_i+2\) 需要断开。于是我们设 \(f_{i,j,k}\) 表示前 \(i\) 个点,当前连续段长度为 \(j\),上一个连续段长度为 \(k\) 的最小花费。
不难发现,两个连续段的值差 \(1\) 是最优的,差的更多只会更劣。
然后转移就是分讨一下当前 \(j\) 是否大于 \(1\),而且转移加上的东西是可以前缀和优化的,于是就做完了。
时间复杂度 \(O(n^3)\)。
AC code:
#include<bits/stdc++.h>
#define int long long
#define N 505
#define pii pair<int,int>
#define x first
#define y second
#define mod 1000000000
#define inf 2e18
using namespace std;
int T=1,n,a[N][N],f[2][N][N],s1[N][N],s2[N][N];
void solve(int cs){
cin>>n;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(i!=j)cin>>a[i][j];
s1[i][j]=s1[i-1][j]+a[i][j];
s2[i][j]=s2[i][j-1]+a[i][j];
}
}
memset(f[1],0x3f,sizeof f[1]);
f[1][1][0]=0;
for(int i=2;i<=n;i++){
memset(f[i&1],0x3f,sizeof f[i&1]);
for(int j=2;j<=i;j++){
for(int k=1-(j==i);k<=i-j;k++){
f[i&1][j][k]=f[i-1&1][j-1][k]+s1[i-1][i]-s1[i-j][i]+s2[i][i-j-k];
}
}
for(int k=1;k<i;k++){
for(int l=1-(k==i-1);l<=i-k;l++){
f[i&1][1][k]=min(f[i&1][1][k],f[i-1&1][k][l]+s2[i][i-1-k]);
}
}
}
int res=inf;
for(int j=1;j<=n;j++){
for(int k=1-(j==n);k<=n-j;k++){
res=min(res,f[n&1][j][k]);
}
}
cout<<res<<'\n';
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
// cin>>T;
// init();
for(int cs=1;cs<=T;cs++){
solve(cs);
}
return 0;
}

浙公网安备 33010602011771号