Atcoder Grand Contest 036 D - Negative Cycle
Atcoder Grand Contest 036 D - Negative Cycle
解题思路
在某些情况下,给一张图加或删一些边要使图合法的题目要考虑到最短路的差分约束系统。这一题看似和最短路没什么关系,但有一个不那么经典的推论,对于一个点 \(u\) 不在负环上的一个充要条件是
其中 \(S\) 是图中任意与 \(u\) 联通的一点。
随便新建一个源点 \(S\),我们令 \(p_i=dis(S,i)\) ,仅考虑原图的链可以得到 \(p_i \geq p_{i+1}\) 。对于任意两点 \(x,y\ (x<y)\) ,新加的边 \((x, y), (y, x)\) 需分别满足 \(p_x-1\geq p_y,p_y+1\geq p_x\) 。这里看似推不下去了然而巧妙差分后能获得非常显然的结论,令 \(q_i=p_i-p_{i+1}\) ,移项可得
然后我们可以证明出,\(q_i \in \{0,1\}\),这里比较容易,如果 \(q_i <0\) 原链的差分约束条件就不满足,如果 \(q_i > 0\) 则点 \(i+1\) 存在额外的 \(-1\) 入边 \((v,i+1),v< i\),此时 \(v\) 到 \(i\) 最坏情况可以走一段 \(0\) 链更新,所以 \(q_i\) 最多只能为 \(1\) 。
然后我们就可以考虑 \(q_i\) 的每一位取 \(0\) 还是取 \(1\) ,然后删掉不合法的边,这个过程是可以 \(\text{DP}\) 解决的,对于不满足 \(\sum q_i \leq 1\) 的情况,在其跨过第二个 \(1\) 的时候统计掉,对于 \(\sum q_i \geq 1\) 的情况,对于每一段连续的 \(0\) 统计即可,那么就可以令 \(dp[i][j]\) 为当前考虑到前 \(i\) 位且 \(i\) 选 \(1\),上一个 \(1\) 在 \(j\) 的答案,转移使用前缀和优化即可。
code
/*program by mangoyang*/
#include<bits/stdc++.h>
#define inf (0x7f7f7f7f)
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Min(a, b) ((a) < (b) ? (a) : (b))
typedef long long ll;
using namespace std;
template <class T>
inline void read(T &x){
int ch = 0, f = 0; x = 0;
for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1;
for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
if(f) x = -x;
}
const int N = 505;
#define int ll
int A[N][N], B[N][N], C[N][N], D[N][N], dp[N][N], n;
signed main(){
read(n);
for(int i = 1; i <= n; i++){
for(int j = 1; j < i; j++) read(A[j][i]);
for(int j = i + 1; j <= n; j++) read(B[i][j]);
}
for(int i = 0; i <= n + 1; i++)
for(int j = i; j <= n + 1; j++){
if(i) C[i][j] += C[i-1][j];
for(int k = j; k <= n + 1; k++) C[i][j] += A[i][k];
}
for(int i = n + 1; i >= 0; i--)
for(int j = i; j <= n + 1; j++){
D[i][j] += D[i+1][j];
for(int k = i; k <= j; k++) D[i][j] += B[i][k];
}
memset(dp, 0x3f, sizeof(dp));
dp[0][0] = 0;
for(int i = 1; i <= n + 1; 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] + C[j][i+1] - C[k][i+1] + D[j+1][i]);
}
int ans = inf;
for(int i = 0; i <= n; i++)
ans = min(ans, dp[n+1][i]);
cout << ans << endl;
return 0;
}