AtCoder agc036_d Negative Cycle

题目传送门

发现图中有没有负环的判定是困难的(也不能说难,但是判定的话很慢)。考虑有什么更好的方法,发现图中没有负环等价于差分约束有解。

现在转化一下题目中的条件:

  • \(x_i\ge x_{i+1}\)

  • \(i<j\) 时,\(x_i-1\ge x_j\)

  • \(i>j\) 时,\(x_i+1\ge x_j\)

然后发现,若我们保留 \(i<j\)\(i\rightarrow j\) 边,则 \(i<k<j\)\(i\rightarrow k\) 边。\(i>j\) 时同理。

然后发现,对于一个位置 \(i\),他向后的连边当且仅当 \(x_i=x_j\) 需要断开,向前的连边当且仅当 \(x_j\ge x_i+2\) 需要断开。于是我们设 \(f_{i,j,k}\) 表示前 \(i\) 个点,当前连续段长度为 \(j\),上一个连续段长度为 \(k\) 的最小花费。

不难发现,两个连续段的值差 \(1\) 是最优的,差的更多只会更劣。

然后转移就是分讨一下当前 \(j\) 是否大于 \(1\),而且转移加上的东西是可以前缀和优化的,于是就做完了。

时间复杂度 \(O(n^3)\)

AC code:

#include<bits/stdc++.h>
#define int long long
#define N 505
#define pii pair<int,int>
#define x first
#define y second
#define mod 1000000000
#define inf 2e18
using namespace std;
int T=1,n,a[N][N],f[2][N][N],s1[N][N],s2[N][N];
void solve(int cs){
	cin>>n;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			if(i!=j)cin>>a[i][j];
			s1[i][j]=s1[i-1][j]+a[i][j];
			s2[i][j]=s2[i][j-1]+a[i][j];
		}
	}
	memset(f[1],0x3f,sizeof f[1]);
	f[1][1][0]=0;
	for(int i=2;i<=n;i++){
		memset(f[i&1],0x3f,sizeof f[i&1]);
		for(int j=2;j<=i;j++){
			for(int k=1-(j==i);k<=i-j;k++){
				f[i&1][j][k]=f[i-1&1][j-1][k]+s1[i-1][i]-s1[i-j][i]+s2[i][i-j-k];
			}
		}
		for(int k=1;k<i;k++){
			for(int l=1-(k==i-1);l<=i-k;l++){
				f[i&1][1][k]=min(f[i&1][1][k],f[i-1&1][k][l]+s2[i][i-1-k]);
			}
		}
	}
	int res=inf;
	for(int j=1;j<=n;j++){
		for(int k=1-(j==n);k<=n-j;k++){
			res=min(res,f[n&1][j][k]);
		}
	}
	cout<<res<<'\n';
}
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
//	cin>>T;
//	init();
	for(int cs=1;cs<=T;cs++){
		solve(cs);
	}
	return 0;
}
posted @ 2025-05-12 12:08  zxh923  阅读(18)  评论(0)    收藏  举报