• 【luogu AT5147】Negative Cycle(差分约束)(DP)


    Negative Cycle

    题目链接:luogu AT5147

    题目大意

    给你一个有向图,有 i 到 i+1 的边,边权为 0。
    然后对于不相等的 i,j 之间,有一条 i 到 j 的边,如果从小到大边权为 -1,否则为 1,然后可以被你删去,有一个费用 ai,j。
    然后要你用最小的费用使得图中不存在负环。

    思路

    首先由一个转化就是看到负环那存在的条件就是差分约束无解。
    (因为你发现正常来搞好像不太能看负环之类的)

    然后看到有不能被删的边,考虑从它下手,发现条件是 x i ⩾ x i + 1 x_i\geqslant x_{i+1} xixi+1

    然后接着看可以删掉的:
    i → j ( i < j ) : x i − 1 ⩾ x j i\rightarrow j(iij(i<j):xi1xj
    i → j ( i > j ) : x i + 1 ⩾ x j i\rightarrow j(i>j):x_i+1\geqslant x_j ij(i>j):xi+1xj

    然后因为好观察所以我们让 i < j ii<j,那应该是:
    i → j ( i < j ) : x i − 1 ⩾ x j i\rightarrow j(iij(i<j):xi1xj
    j → i ( i < j ) : x j + 1 ⩾ x i j\rightarrow i(iji(i<j):xj+1xi
    整理一下:
    x i − x j ⩾ 1 x_i-x_j\geqslant 1 xixj1
    x i − x j ⩽ 1 x_i-x_j\leqslant 1 xixj1

    考虑怎么跟上面的那个联系起来,发现上面那个可以变成:
    x i − x i + 1 ⩾ 0 x_{i}-x_{i+1}\geqslant 0 xixi+10
    然后就是类似于差分的形式,然后那连续的差分加起来就是头减尾。
    那不就跟上面的那个一样了吗!
    y i = x i − x i + 1 ⩾ 0 y_i=x_i-x_{i+1}\geqslant 0 yi=xixi+10,那两个条件就是 ∑ p = i j y i ⩾ 1 \sum\limits_{p=i}^jy_i\geqslant 1 p=ijyi1 ⩽ 1 \leqslant 1 1

    那也就是条件是不能 < 1 , > 1 <1,>1 <1,>1 这两种,那显然的我们的取值由于 y ⩾ 0 y\geqslant 0 y0 就只能是 0 / 1 0/1 0/1(整数)。
    那我们可以看到我们所要注意的就是相邻的 1 1 1 之间的位置,具体而言我们放一个 1 1 1 的时候要注意前面两个 1 1 1 的位置。
    因为两个 1 1 1 之间是 < 1 <1 <1 要用贡献删去。
    到前面的那个 1 1 1 的位置的时候积累了两个 1 1 1,就也要用贡献。

    f i , j f_{i,j} fi,j 为倒数那个在 i i i,倒数两个在 j j j,DP 转移即可。
    至于转移系数那些,考虑每次到一个位置,那之前的 f i , j f_{i,j} fi,j 加贡献因为无论放不放这个位置到前面那些的都会有,然后在看是不是放把下标转移一下。
    那你加的是一段的贡献,这个简单直接前缀和预处理一下就可以了。

    代码

    #include
    #include
    #include
    #define ll long long
    
    using namespace std;
    
    const int N = 505;
    int n, a[N][N];
    ll f[N][N], ans, sb[N][N], bs[N][N];
    //sb:small big
    //bs:big small
    
    int main() {
    	scanf("%d", &n);
    	for (int i = 1; i <= n; i++)
    		for (int j = 1; j <= n; j++) {
    			if (i == j) continue;
    			scanf("%d", &a[i][j]);
    		}
    	
    	for (int i = 1; i <= n; i++)
    		for (int j = i; j <= n; j++)
    			sb[i][j] = sb[i - 1][j] + a[i][j];
    	for (int i = 1; i <= n; i++)
    		for (int j = 1; j <= i; j++)
    			bs[i][j] = bs[i][j - 1] + a[i][j];
    	
    	memset(f, 0x7f, sizeof(f));
    	f[1][1] = 0;
    	for (int i = 1; i <= n; i++) {
    		for (int j = 1; j < i; j++)
    			for (int k = 1; k <= j - !!(j - 1); k++)
    				f[i][j] = min(f[i][j], f[j][k]);
    		for (int j = 1; j <= i; j++)
    			for (int k = 1; k <= j - !!(j - 1); k++)
    				f[j][k] += (sb[i][i] - sb[j - 1][i]) + (bs[i][k - 1]);
    	}
    	ans = f[0][0];
    	for (int i = 1; i <= n; i++)
    		for (int j = 1; j <= n; j++)
    			ans = min(ans, f[i][j]);
    	printf("%lld", ans);
    		
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
  • 相关阅读:
    01科技文献
    使用并查集实现查找无向图的连通分量和求解有向图的强连通分量
    正则表达式总结
    安科瑞Acrel-7000工业能耗在线监测系统,企业能源管控平台
    【基于OpenHarmony的智能学习桌面项目中遇到的问题及解决办法】
    微短剧:爱优腾、抖快、喜马拉雅的新航线
    智慧火灾应急救援:无人机、直升机航拍视角下的火灾应急救援检测数据集&代码
    【Python】《Python编程:从入门到实践 (第2版) 》笔记-Chapter2-变量和简单数据类型
    vue3 vite 打包 二级目录刷新空白
    MyBatis-Plus(一)
  • 原文地址:https://blog.csdn.net/weixin_43346722/article/details/126796778