bnu 51644 Whalyzh's Problem(网络流,最大密度图) (北师16校赛)

本文介绍了一个关于最大密度子图的问题及其解决方案。该问题要求在给定矩阵中找到由0和1组成的向量,使得选中的数的平均值最大。通过构建特定图模型并运用最大流算法求解。

很久以前,当whalyzh同学是一个萌新的时候,遇到了这么一个问题:

给定长为n的序列B,构造一个只有0和1的长为n的序列A,使得\sum_{i=0}^{n-1}{A[i] \times B[i]}的值最大。

小Q同学想了一秒钟之后说:这不是一眼题么?然后whalyzh同学瞬间就会了。

过了几天,当whalyzh同学还是一个萌新的时候,遇到了这么一个问题:

给定n阶方阵B,构造一个只有0和1的1 \times n的向量A,使得ABA^T=\sum_{i=0}^{n-1}\sum_{j=0}^{n-1}{A[i] \times B[i][j] \times A[j]}的值最大。

小Q同学想了一分钟之后说:这不是一眼题么?然后whalyzh同学瞬间就会了。

又过了几天,当whalyzh仍然是一个萌新的时候,遇到了这么一个问题:

给定n阶方阵B,构造一个只有0和1的1 \times n的向量A,使得(ABA^T-\sum_{i=0}^{n-1}{A[i] \times B[i][i])/\sum_{i=0}^{n-1}{A[i]}的值最大。

小Q同学想了一小时之后说:不能总是问我,你得自己去思考。然后whalyzh同学瞬间就懵逼了。

即使到现在,whalyzh同学仍然百思不得其解,希望你能帮他解决这个问题。

请注意,在这个问题中,规定0/0=0

Input

第一行是一个正整数T(\leq 50),表示测试数据的组数,

对于每组测试数据,

第一行是一个整数n(1 \leq n \leq 100),表示方阵的大小,

接下来n行,每行n个整数b[i][j](0 \leq b[i][j] \leq 10),表示方阵中的数。

Output

对于每组测试数据,输出所求的最大值,答案精确到小数点后五位。

Sample Input

1
3
0 1 0
1 2 4
1 3 2

Sample Output

3.50000

Hint

对于样例,令A[0]=0,A[1]=1,A[2]=1,可取得最大值7/2=3.50000

Source

Author

hwq1352249

题目大意:

       给出一个矩阵B(元素小于等于10)和一个由01组成的向量A,能选择其中的B[i][j]当且仅当A[i]=A[j]=1且i!=j,给定B通过A中把一些数置为1来选择B中的数使得B中数的和比上A中1的个数最大。

解题思路:

       原问题等价于给定一个n个顶点的图,每两点之间有一条边,边权为1到10,然后从中选出一些点,使的这些点的诱导子图的边权和/点数尽可能大,这是一个最大密度子图问题。

感谢chitanda的代码~



#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
#define N 100
const double eps = 1e-8;
const double inf = 1e+8;
struct Edge { int v; double w; Edge *x; } *e, *lnk[N * N + 9], E[12 * N * N + 9];
int TT, S, T, n, m, b[N + 9][N + 9];
int g[N * N + 9], h[N * N + 9];

int dcmp(double x) {
	return (x > +eps) - (x < -eps);
}

void add(int u, int v, double w) {
	e->v = v, e->w = w, e->x = lnk[u], lnk[u] = e++;
	e->v = u, e->w = 0, e->x = lnk[v], lnk[v] = e++;
}

double sap(int u, double flw) {
	if (u == T) return flw;
	double det, sum = .0;
	for (Edge *p = lnk[u]; p; p = p->x)
		if (h[u] == h[p->v] + 1 && dcmp(p->w)) {
			det = sap(p->v, min(p->w, flw - sum));
			p->w -= det;
			E[(p - E) ^ 1].w += det;
			if (dcmp((sum += det) - flw) == 0) return sum;
		}
	if (h[S] >= m) return sum;
	if (--g[h[u]] == 0) h[S] = m;
	++g[++h[u]];
	return sum;
}

bool check(double x) {
	m = n + n * (n - 1) / 2;
	S = ++m, T = ++m;

	e = E;
	for (int i = 1; i <= m; ++i) lnk[i] = 0;
	for (int i = 1; i <= n; ++i) add(i, T, x);

	int cnt = n;
	double sum = .0;
	for (int i = 1; i < n; ++i)
		for (int j = i + 1; j <= n; ++j) {
			++cnt;
			if (b[i][j]) {
				sum += b[i][j];
				add(S, cnt, b[i][j]);
			}
			add(cnt, i, inf);
			add(cnt, j, inf);
		}
	g[0] = m;
	for (int i = 1; i <= m; ++i) g[i] = h[i] = 0;
	while (h[S] < m) sum -= sap(S, inf);
	return sum > eps;
}

int main() {
	scanf("%d", &TT);
	while (TT--) {
		scanf("%d", &n);
		memset(b, 0, sizeof b);
		for (int i = 1; i <= n; ++i)
			for (int j = 1; j <= n; ++j) 
			{
				int x;
				scanf("%d", &x);
				if (i < j) b[i][j] += x;
				if (i > j) b[j][i] += x;
			}
		double l = 0, r = 1000, m;
		while (fabs(r - l) > eps) 
		{
			m = (l + r) / 2;
			if (check(m)) l = m;
			else r = m;
		}
		printf("%.5f\n", l);
	}
	return 0;
}


### 最大流问题的算法与解决方案 最大流问题(Maximum Flow Problem)是网络流理论中的一个经典问题,目标是在给定的流网络中找到从源点到汇点的最大可能流量。该问题的核心在于满足网络中各边容量约束的同时,最大化通过网络的总流量。 #### 常见算法及其特点 1. **Ford-Fulkerson算法** Ford-Fulkerson算法是一种基于增广路径的贪心算法。其基本思想是从初始流开始,不断寻找残余网络中的增广路径,并沿该路径增加流量,直到无法找到新的增广路径为止。该算法的时间复杂度取决于增广路径的选择方式[^1]。如果使用广度优先搜索(BFS)来寻找增广路径,则可以得到Edmonds-Karp算法,其时间复杂度为 \(O(VE^2)\),其中 \(V\) 是节点数,\(E\) 是边数。 2. **Dinic算法** Dinic算法是对Ford-Fulkerson算法的一种改进,它利用分层的概念,在每次迭代中同时寻找多条不相交的增广路径。Dinic算法的时间复杂度为 \(O(V^2E)\)。由于其高效性,Dinic算法在处理稠密时表现尤为出色[^1]。 3. **Push-Relabel算法** Push-Relabel算法采用预流的思想,通过“推”和“重贴标签”两种操作来调整流值,直到达到最大流状态。该算法的时间复杂度为 \(O(V^2E)\)。在实际应用中,Push-Relabel算法通常比Dinic算法更快,尤其是在稀疏上[^2]。 4. **最小费用最大流算法** 如果在网络流问题中引入了单位流量的费用属性,则需要求解最小费用最大流问题。常用算法包括SPFA算法和Dijkstra算法结合二分堆实现的优化版本。这些算法在寻找增广路径时不仅考虑流量,还考虑路径的费用,从而确保最终解既满足最大流量要求,又具有最小费用[^1]。 #### 算法选择依据 不同算法适用于不同的场景。对于稀疏,Push-Relabel算法通常表现更优;而对于稠密,Dinic算法可能是更好的选择。此外,当问题涉及费用时,应选用最小费用最大流算法。选择合适的算法可以显著提高求解效率。 ```python # 示例代码:Dinic算法实现最大流 class Edge: def __init__(self, v, flow, rev): self.v = v self.flow = flow self.rev = rev def bfs(s, t, level, graph): from collections import deque queue = deque([s]) level[s] = 0 while queue: u = queue.popleft() for i in range(len(graph[u])): e = graph[u][i] if e.flow > 0 and level[e.v] == -1: level[e.v] = level[u] + 1 queue.append(e.v) return level[t] != -1 def dfs(u, t, f, level, iter, graph): if u == t: return f for i in range(iter[u], len(graph[u])): iter[u] = i e = graph[u][i] if e.flow > 0 and level[u] < level[e.v]: d = dfs(e.v, t, min(f, e.flow), level, iter, graph) if d > 0: e.flow -= d graph[e.v][e.rev].flow += d return d return 0 def dinic_max_flow(graph, s, t): n = len(graph) max_flow = 0 while True: level = [-1] * n if not bfs(s, t, level, graph): break iter = [0] * n f = dfs(s, t, float('inf'), level, iter, graph) while f > 0: max_flow += f f = dfs(s, t, float('inf'), level, iter, graph) return max_flow ``` ### 实验结论与体会 网络流问题广泛应用于现实生活中的各种分配与调度问题,如论文评审、排班安排等。掌握最大流算法的思想及其多种实现方式,能够有效解决这些问题。同时,理解流网络中的专有名词和相关定理是解决问题的基础[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值