[AGC036D] Negative Cycle
图没有负环等价于存在一组合法的差分约束的解
存在 \((i,i+1,0)\),得出 \(x_i \ge x_{i+1}\) ,那么记 \(d_{i}=x_i-x_{i+1} \ge 0\)
然后分析两种边,我们希望尽量少的边被删去
-
\(i<j\), \(x_i -x_j \ge 1\) , \(d_i+d_{i+1}+...+d_{j-1} \ge 1\)
这种边被删去当且仅当 \(\forall d_i=0\)
-
\(i >j\),\(x_j - x_i \le 1\) , \(d_j+d_{j+1}+...+d_{i-1} \le 1\)
这种边被删去当且仅当 \(\sum d_i \ge 2\)
那么最优情况下 \(d_i\) 的取值只能为 \(0,1\)
注意到第一个限制为 \(0\) 连续段,那么可以设计这样一个 dp:
\(dp_{i,j}\) : 确定前 \(i\) 个 \(d\) , \(d_i=1\),上一个 \(d=1\) 的位置为 \(j\) 的最小代价
转移枚举 \(0\) 连续段,可以使用前缀和做到 \(O(1)\) 转移。
时间复杂度 \(\mathcal O(n^3)\)
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
#define ll long long
const int MAXN = 500;
int n , a[ MAXN + 5 ][ MAXN + 5 ];
ll S1[ MAXN + 5 ][ MAXN + 5 ] , S2[ MAXN + 5 ][ MAXN + 5 ];
ll Calc1( int l , int r ) { //i<j
return S1[ r ][ r ] - S1[ l - 1 ][ r ];
// ll ans = 0;
// for( int i = l ; i <= r ; i ++ )
// for( int j = i + 1 ; j <= r ; j ++ )
// ans += a[ i ][ j ];
// return ans;
}
ll Calc2( int k , int l , int r ) { //i>j
return S2[ r ][ k ] - S2[ l - 1 ][ k ];
// ll ans = 0;
// for( int i = l ; i <= r ; i ++ )
// for( int j = 1 ; j <= k ; j ++ )
// ans += a[ i ][ j ];
// return ans;
}
ll dp[ MAXN + 5 ][ MAXN + 5 ];
int main( ) {
scanf("%d",&n);
for( int i = 1 ; i <= n ; i ++ )
for( int j = 1 ; j <= n ; j ++ )
if( i != j ) scanf("%d",&a[ i ][ j ]);
for( int i = 1 ; i <= n ; i ++ )
for( int j = 1 ; j <= n ; j ++ )
S1[ i ][ j ] = S1[ i - 1 ][ j ] + S1[ i ][ j - 1 ] - S1[ i - 1 ][ j - 1 ] + ( i < j ? a[ i ][ j ] : 0 );
for( int i = 1 ; i <= n ; i ++ )
for( int j = 1 ; j <= n ; j ++ )
S2[ i ][ j ] = S2[ i - 1 ][ j ] + S2[ i ][ j - 1 ] - S2[ i - 1 ][ j - 1 ] + ( i > j ? a[ i ][ j ] : 0 );
ll ans = 1e18;
memset( dp , 0x3f , sizeof( dp ) ); dp[ 0 ][ 0 ] = 0;
for( int i = 1 ; i <= n ; i ++ )
for( int j = 0 ; j < i ; j ++ ) {
for( int k = 0 ; k <= j ; k ++ )
dp[ i ][ j ] = min( dp[ i ][ j ] , dp[ j ][ k ] + Calc1( j + 1 , i ) + Calc2( k , j + 1 , i ) );
ans = min( ans , dp[ i ][ j ] + Calc1( i + 1 , n ) + Calc2( j , i + 1 , n ) );
}
printf("%lld\n", ans );
return 0;
}