Isap --最大收益问题

题目描述

永乐理工大学的实验室计划了一系列实验项目,这些实验项目需要不同的实验仪器,而配置仪器需要费用,但实验也会产生经济效益。请设计一个算法,如何才能有最大净收益?


思路分析

  1. 模型归纳为二分图匹配模型,假设实验项目为E = {E1,E2,E3,…,Ei},仪器为{I1,I2,I3,…,Ii},那么假设配置仪器的费用使用Ci表示,经济收益用Pi表示,那么,我们利用二分图实现该问题,首先源点与汇点需要增加这两个点,从源点到实验项目Ei的边的容量为经济效益,而从实验仪器到汇点的容量为费用,并且实验项目到该实现项目所需的实验仪器的连线容量设置为无穷大,那么如下公式所示:

在这里插入图片描述
其中集合S是选中的实验以及用到的仪器,集合T为未选中的仪器即实验
2. 其中Ck为我们使用的仪器的费用,pi是实验的费用,如下例:
在这里插入图片描述
假设选取E1,E2,E3三个实验,并且需要仪器U1,U2,U3那么计算式子就是总收益 = p1 + p2 + p3 + p4 ,
未选中的实验收益p4,仪器费用c1 + c2 + c3,那么总收益需要减去 p4 + c1+ c2 + c3,那么这几项之和正好是该网络的割线,那么只需要求解最小割流量即可,而在离散数学中可知,最大流等于最小割,那么我们只需要求解该网络的最大流量即可求解。
计算表达式 = 所有实验的收益 - 最大流量值;


算法分析

  1. 构建网络,从源点到实验项目Ei的边的容量为经济效益,而从实验仪器到汇点的容量为费用,实验与仪器之间的连线设置为无穷大。
  2. 求解最大网络流,Isap算法即可解决
  3. 输出方案即可

主体代码解析

  1. 加边
void add(int u, int v, int num)
{
	add_edge(u, v, num);
	add_edge(v, u, 0);
}
void add_edge(int u, int v, int num)
{
	edge[current_num].contain = num;
	edge[current_num].flow = 0;
	edge[current_num].start = v;
	edge[current_num].next = vertex[u].first;
	vertex[u].first = current_num++;
	return;
}
  1. 高度设置
void set_height(int start, int total)
{
	queue<int> q;
	memset(_floor, -1, sizeof(_floor));
	memset(_count, 0, sizeof(_count));
	_floor[start] = 0;
	q.push(start);
	while (!q.empty())
	{
		int current = q.front();
		q.pop();
		++_count[_floor[current]];
		for (int i = vertex[current].first; i != -1; i = edge[i].next)
		{
			int u = edge[i].start;
			if (_floor[u] == -1)
			{
				_floor[u] = _floor[current] + 1;
				q.push(u);
			}
		}
	}
	cout << "初始化高度是:" << endl;
	cout << "_floor[ ] = ";
	for (int i = 1; i <= total; i++)
		cout << "  " << _floor[i];
	cout << endl;
	return;
}
  1. Isap算法实现
int Isap(int start, int total, int point_total)
{
	set_height(total, point_total);
	int ans = 0, u = start;
	int d;
	while (_floor[start] < point_total)
	{
		int v = vertex[u].first;
		if (u == start)
			d = INF;
		for (; v != -1; v = edge[v].next){
			int point = edge[v].start;
			if (edge[v].flow < edge[v].contain && _floor[point] + 1 == _floor[u]){
				u = point;
				pre_num[point] = v;
				d = min(d, edge[v].contain - edge[v].flow);
				if (u == total){
					cout << "增广路径是:" << total;
					while (u != start){//更新
						int j = pre_num[u];
						edge[j].flow += d;
						edge[j + 1].flow -= d;
						u = edge[j + 1].start;
						cout << "--" << u;
					}
					cout << "增流:" << d << endl;
					ans += d;
					d = INF;
				}
				break;
			}
		}
		if (v == -1){
			if (--_count[_floor[u]] == 0)
				break;
			int hmin = total;
			for (int j = vertex[u].first; j != -1; j = edge[j].next)
				if (edge[j].contain > edge[j].flow)
					hmin = min(hmin, _floor[edge[j].start]);
			_floor[u] = hmin + 1;
			cout << "重贴标签后的高度是:" << endl;
			cout << "_floor[ ] = ";
			for (int i = 1; i <= point_total; i++)
				cout << "  " << _floor[i];
			cout << endl;
			++_count[_floor[u]];
			if (u != start)//后退一步
				u = edge[pre_num[u] + 1].start;
		}
	}
	return ans;
}
  1. 方案搜索
void DFS(int u)
{
	for (int i = vertex[u].first; i != -1; i = edge[i].next){
		if (edge[i].contain > edge[i].flow){
			int u = edge[i].start;
			if (!flag[u]){
				flag[u] = 1;
				DFS(u);
			}
		}
	}
	return;
}
发布了166 篇原创文章 · 获赞 45 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_44116998/article/details/104105592