[AGC036D] Negative Cycle

图没有负环等价于存在一组合法的差分约束的解

存在 (i,i+1,0),得出 xixi+1 ,那么记 di=xixi+10

然后分析两种边,我们希望尽量少的边被删去

  • i<jxixj1 , di+di+1+...+dj11

    这种边被删去当且仅当 di=0

  • i>jxjxi1dj+dj+1+...+di11

    这种边被删去当且仅当 di2

那么最优情况下 di 的取值只能为 0,1

注意到第一个限制为 0 连续段,那么可以设计这样一个 dp:

dpi,j : 确定前 iddi=1,上一个 d=1 的位置为 j 的最小代价

转移枚举 0 连续段,可以使用前缀和做到 O(1) 转移。

时间复杂度 O(n3)

#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 @   chihik  阅读(18)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示