• 【PR #5 A】双向奔赴(状压DP)


    双向奔赴

    题目链接:PR #5 A

    题目大意

    给你一个简单无向图,然后你要给每条边定向,每个边每个方向有不一样的费用。
    然后要你用最小的费用使得这张图强连通,如果无法强连通输出 -1。

    思路

    首先考虑最暴力的方法,可以直接找一条路径从 1 1 1 出发经过所有点(可以重复经过点),然后回到 1 1 1

    然后我们简化一下题意,我们可以默认选边权小的那个,然后如果要选另一边就要加上差的费用,然后最后加上边权小和。

    然后发现我们其实可以把它分割,分割成若干个环。
    具体一点就是你已经有一个强连通的子集,然后你每次从一个子集里面的点往外走,一直走没有见过的,然后最后走回到子集里面,然后你外面见到的点也就也跟着强连通了。(这个一直不走见过的是没问题的,因为走见过的你可以把它拆成若干个没有见过的)
    然后就有一个大概的状压想法了: g S g_S gS 表示当前 S S S 这个子集强连通的最小费用, f S , i , j f_{S,i,j} fS,i,j S S S 这个子集强连通,环的终点是 i i i,现在在 j j j
    (然后为了我们能表示出走出环的点,所以 f S , i , j f_{S,i,j} fS,i,j S S S 就表示成强连通的子集与现在要加上的点的并集)
    然后就是三种,一个是从 g g g f f f,接着是 f f f 内部转移,最后 f f f g g g

    复杂度为 O ( n 3 2 n ) O(n^32^n) O(n32n) 能过。

    然后你会发现锅了,因为你这样是不能保证一条边只会从一个方向走的。
    (就比如你从 g g g 走一个点到 f f f 结果你直接从 f f f 走回 g g g 了)
    那我们考虑怎么办,因为这个时候复杂度已经是很大的了不能再乘 n n n,考虑压缩一下,会发现你 f S , i , j f_{S,i,j} fS,i,j j j j 是一定在 S S S 中的,那我们能不能不在呢?
    自然是可以的,那如果我们用不在的方式转移,但是一开始又在呢?
    你会发现就不能直接归到答案里面(因为你答案枚举 j j j 是要不在 S S S 里面的),而是要通过走一步消除掉这个特异性(因为你无论怎样都是正常转移,那下一个点肯定不在点集里面),所以就可以走回去了。
    然后就可以用这个很妙的方法特判掉这个情况啦!
    (具体实现可以看代码)

    代码

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    
    using namespace std;
    
    const int N = 18;
    int n, a[N][N], f[1 << N][N][N], g[1 << N], ans;
    
    int main() {
    	scanf("%d", &n);
    	for (int i = 0; i < n; i++)
    		for (int j = 0; j < n; j++)
    			scanf("%d", &a[i][j]);
    	for (int i = 0; i < n; i++)
    		for (int j = 0; j < i; j++)
    			if (a[i][j] != -1) {
    				int x = min(a[i][j], a[j][i]);
    				a[i][j] -= x; a[j][i] -= x; ans += x;
    			}
    	
    	memset(f, 0x7f, sizeof(f));
    	memset(g, 0x7f, sizeof(g)); int inf = g[0];
    	g[1] = 0;
    	for (int S = 1; S < (1 << n); S++) {
    		if (g[S] < inf) {
    			for (int i = 0; i < n; i++) if ((S >> i) & 1)
    				for (int s = 0; s < n; s++) if ((S >> s) & 1)
    					for (int j = 0; j < n; j++) if (!((S >> j) & 1) && a[s][j] != -1) {
    						if (s != i) f[S][i][j] = min(f[S][i][j], g[S] + a[s][j]);
    							else f[S | (1 << j)][i][j] = min(f[S | (1 << j)][i][j], g[S] + a[s][j]);//不能从起点出发到这个点就直接到终点 
    					}
    		}
    		for (int i = 0; i < n; i++) if ((S >> i) & 1)
    			for (int j = 0; j < n; j++) if (f[S][i][j] < inf)
    				for (int k = 0; k < n; k++) if (!((S >> k) & 1) && a[j][k] != -1)
    					f[S | (1 << j)][i][k] = min(f[S | (1 << j)][i][k], f[S][i][j] + a[j][k]);
    		for (int i = 0; i < n; i++) if ((S >> i) & 1)
    			for (int j = 0; j < n; j++) if (!((S >> j) & 1) && f[S][i][j] < inf && a[j][i] != -1)
    				g[S | (1 << j)] = min(g[S | (1 << j)], f[S][i][j] + a[j][i]);
    	}
    	
    	if (g[(1 << n) - 1] < inf) ans += g[(1 << n) - 1];
    		else ans = -1;
    	printf("%d", 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
    • 47
    • 48
  • 相关阅读:
    MySQL的常用优化方案
    计算机毕业设计Python+Django的优智学直播授课平台系统(源码+系统+mysql数据库+Lw文档)
    【数据结构与算法】深度剖析“八大排序”(上)_ 探寻一些不为人知的细节
    软件需求文档、设计文档、开发文档、运维文档大全
    【23真题】C9无歧视,专业课均分130!
    【广州华锐互动】煤矿设备AR远程巡检系统实现对井下作业的远程监控和管理
    jmeter疑难杂症
    ArcGIS Pro怎么生成高程点
    jenkins常见问题
    [跨数据库、微服务] FreeSql 分布式事务 TCC/Saga 编排重要性
  • 原文地址:https://blog.csdn.net/weixin_43346722/article/details/125461633