AT5147-[AGC036D]Negative Cycle【dp,模型转换】

正题

题目链接:https://www.luogu.com.cn/problem/AT5147


题目大意

\(n\)个点的一张图,其中\(i\rightarrow i+1(i< n)\)有一条边权值为\(0\)

对于其他\(i,j(i\neq j)\)存在一条边\(i\rightarrow j\),若\(i<j\)那么权值为\(-1\),否则为\(1\)

删除\(i\rightarrow j(i\neq j)\)的代价为\(a_{i,j}\),要求代价最小的情况下使得图中不存在负环。

\(1\leq n\leq 500\)


解题思路

这个容易负环让人一头雾水不知道怎么维护,我们知道差分约束有解的条件就是没有负环,所以我们可以考虑转成差分约束模型。

那么对于不能删除的边\(i\rightarrow i+1\)就有限制\(x_i\geq x_{i+1}\),也就是整个序列单调不升。

然后形如\(i\rightarrow j(i<j)\)可以视为\(x_i-1\geq x_j\)
形如\(i\leftarrow j(i<j)\)可以视为\(x_i\leq x_j+1\)

也就是\(x_i-x_j\leq 1\)\(x_i-x_j\geq 1\)的限制,我们考虑维护差分数组(反过来的)\(y_i=x_{i-1}-x_{i}\),那么条件就是区间和不能大于/小于\(1\),显然的那么\(y_i\)就只有可能是\(0/1\)

然后仔细观察这个限制会发现其实只有相邻的\(1\)会有用,我们考虑\(dp\)来处理,设\(f_{i,j,k}\)表示做到第\(i\)个,上一个\(1\)\(j\)处,再上一个\(1\)\(k\)处。

前缀和一下\(a\)数组优化转移即可。

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


code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=510;
ll n,a[N][N],s[N][N],t[N][N],f[N][N],ans;
signed main()
{
	scanf("%lld",&n);
	for(ll i=1;i<=n;i++)
		for(ll j=1;j<=n;j++){
			if(i==j)continue;
			scanf("%lld",&a[i][j]);
		}
	for(ll i=1;i<=n;i++)
		for(ll j=1;j<=i;j++)
			s[i][j]=s[i][j-1]+a[j][i],
			t[i][j]=t[i][j-1]+a[i][j];
	memset(f,0x3f,sizeof(f));
	ans=f[0][0];f[1][1]=0;
	for(ll i=2;i<=n;i++){
		for(ll j=1;j<i;j++)
			for(ll k=1;k<=j-(j!=1);k++)
				f[i][j]=min(f[i][j],f[j][k]);
		for(ll j=1;j<=i;j++)
			for(ll k=1;k<=j-(j!=1);k++)
				f[j][k]+=s[i][i]-s[i][j-1],f[j][k]+=t[i][k-1];
	}
	for(ll i=1;i<=n;i++)
		for(ll j=1;j<=n;j++)
			ans=min(ans,f[i][j]);
	printf("%lld\n",ans);
	return 0;
}
posted @ 2022-07-15 19:05  QuantAsk  阅读(38)  评论(0编辑  收藏  举报