[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;
}
posted @ 2022-10-16 19:50  chihik  阅读(14)  评论(0编辑  收藏  举报