[AGC036D] Negative Cycle
图没有负环等价于存在一组合法的差分约束的解
存在 ,得出 ,那么记
然后分析两种边,我们希望尽量少的边被删去
-
, ,
这种边被删去当且仅当
-
, ,
这种边被删去当且仅当
那么最优情况下 的取值只能为
注意到第一个限制为 连续段,那么可以设计这样一个 dp:
: 确定前 个 , ,上一个 的位置为 的最小代价
转移枚举 连续段,可以使用前缀和做到 转移。
时间复杂度
#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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通