qoj14453. 网络改造
qoj14453. 网络改造
给定一个 \(n\) 个点 \(m\) 条边的有向图,你可以:
- 以 \(a_i\) 的代价翻转一条边。
 - 以 \(b_i\) 的代价删去一条边。
 - 以 \(c_i\) 的代价删去一个点。
 
无自环无重边,问至少需要多少代价将这个图变为无环图?
\[n\le 22
\]
首先是一个很强的转化:
由于最终的图是无环图,那么任取其一拓扑序 \(p\)。
那么对于所有边 \(\{(p_i,p_j)|i>j\}\),也就是逆序的点,我们要么删去,要么反转。这同时意味着只有这两种操作的代价中的最小值是有用的。下文边权指代删去和反转操作代价的最小值。
那么原问题转化为对排列 \([1,n]\) 定序,求逆序的边边权之和最小值。
设 \(f(S)\) 表示集合 \(S\) 的答案。
那么对于每个集合 \(S\),枚举 \(x\),有转移:
\[f(S)\leftarrow f(S\setminus x)+w_x(S\setminus x)
\]
其中 \(w_x(S)\) 表示点 \(x\) 指向点集 \(S\) 中的点的所有边的代价之和。
\(f(\cdot)\) 和 \(w(\cdot)\) 都可以在 \(\mathcal O(n2^n)\) 内求出。
最后考虑删点,令 \(d(S)=\sum_{x\in S} c_x\)。
令 \(T\) 表示全集,那么答案就是
\[\min_S d(S)+f(T\setminus S)
\]
code
#include <iostream>
#include <algorithm>
namespace wyx {
const int N = 22, M = 1 << N;
int c[N];
int v[N][N];
int n, m;
int del[M];
int dp[M], w[N][M];
inline void main() {
	std::cin >> n >> m;
	for(int i = 0; i < n; ++i) std::cin >> c[i];
	for(int t = m; t--; ) {
		int x, y, a, b;
		std::cin >> x >> y >> a >> b;
		--x, --y, v[x][y] = std::min(a, b);
	}
	m = 1 << n;
	for(int i = 0; i < m; ++i) dp[i] = 1e9;
	dp[0] = 0;
	for(int i = 0; i < n; ++i) {
		int t = 1 << i;
		auto& w = wyx::w[i];
		w[0] = 0;
		for(int j = 1; j < m; ++j) {
			if(j & t) continue;
			int y = std::__lg(j);
			 w[j] = w[j^(1<<y)] + v[i][y];
		}
	}
	for(int j = 0; j < m; ++j) {
		for(int i = 0; i < n; ++i) {
			int t = 1 << i;
			if(j & t) {
				 dp[j] = std::min(dp[j], dp[j ^ t] + w[i][j ^ t]);
			}
		}
	}
	int ans = dp[m-1];
	for(int i = 1; i < m; ++i) {
		int y = std::__lg(i);
		del[i] = del[i ^ (1<<y)] + c[y];
		ans = std::min(ans, del[i] + dp[(m-1) ^i]);
	}
	std::cout << ans << "\n";
}
};
int main() {
	std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
	wyx::main();
}
本文来自博客园,作者:CuteNess,转载请注明原文链接:https://www.cnblogs.com/CuteNess/p/19168621
                    
                
                
            
        
浙公网安备 33010602011771号