假期 2020.01.24
在网络布线的工程中,有许多电缆,而电缆的粗细不同,流量与费用也不一样,那么如何安排才能获得费用最小且流量最大的网络呢?
因为要满足两个条件,那么我们可从两大方面入手:
int expense_choice[Max_size], pre_choice[Max_size];//最短距离,前驱
int visited[Max_size],visited_count[Max_size];//标记访问数组,入队次数
int max_flow;//最大流
int num;//编号
struct Vertex {//邻接表头节点
int first;
}vertex[Max_size];
struct Edge {
int start,next;//弧尾,下一条邻接边
int contain, flow, expense;//边的最大容量,边的流量,边的花费
}edge[Max_size];
memset(vertex, -1, sizeof(vertex));//邻接表表头初始化为-1
cout << "请输入两个节点(v,w)和两者之间的容量与花费(每项共4个数据):" << endl;
for (int i = 1; i <= edge_count; i++)
{
cin >> v >> w >> we >> e;
add(v, w, we, e);
}
cout << endl;
max_flow = 0;
num = 0;
添加边,num作为全部边的编号,从0开始
void add_edge(int v, int w, int we, int e)//加边,赋值
{
edge[num].start = w;//弧头
edge[num].contain = we;//容量
edge[num].expense = e;//花费
edge[num].flow = 0;//流量
edge[num].next = vertex[v].first;//下一条边序号
vertex[v].first = num ++;//边的序号编号
return ;
}
if (edge[i].contain > edge[i].flow && expense_choice[cur] > expense_choice[temp] + edge[i].expense)//存在可行解
{
expense_choice[cur] = expense_choice[temp] + edge[i].expense;//更新到cur点的花费值
pre_choice[cur] = i;//cur的前驱更新
if (visited[cur] == 0)
{
visited_count[cur]++;//访问次数增加一
q.push(cur);//入队
visited[cur] = 1;//标记
if (visited_count[cur] > vertex_count)//存在负环
return 0;
}
}
for (i = pre_choice[vertex_count]; i != -1; i = pre_choice[edge[i ^ 1].start])//使用i^1的目的是因为我们在存储的时候采取的是正向为i,那么逆向是i+1
{
current_cost = min(current_cost, edge[i].contain - edge[i].flow);
cout << "--" << edge[i ^ 1].start;
}
#include
#include
#include
#include
using namespace std;
constexpr int INF = 0x7fffffff;
constexpr int Max_size = 100;
int expense_choice[Max_size], pre_choice[Max_size];//最短距离,前驱
int visited[Max_size],visited_count[Max_size];//标记访问数组,入队次数
int max_flow;//最大流
int num;//编号
struct Vertex {//邻接表头节点
int first;
}vertex[Max_size];
struct Edge {
int start,next;//弧尾,下一条邻接边
int contain, flow, expense;//边的最大容量,边的流量,边的花费
}edge[Max_size];
void Print(int vertex_count);//输出初始邻接表
void Print_flow(int vertex_count);//输出实际网络流路径
void add_edge(int v, int w, int we, int e);//加边,赋值
void add(int v, int w,int we,int e);//因为是网络混合图,即双向流动,所以两者都需定义
int Search_flow(int vertex_count);//寻找路径
int best_expaflo(int i, int vertex_count);//处理路径
int main()
{
int vertex_count,edge_count,v,w,we,e;
cout << "请输入邻接点个数:";
cin >> vertex_count;
cout << "请输入邻接边数:";
cin >> edge_count;
memset(vertex, -1, sizeof(vertex));//邻接表表头初始化为-1
cout << "请输入两个节点(v,w)和两者之间的容量与花费(每项共4个数据):" << endl;
for (int i = 1; i <= edge_count; i++)
{
cin >> v >> w >> we >> e;
add(v, w, we, e);
}
cout << endl;
max_flow = 0;
num = 0;
Print(vertex_count);//输出初始邻接表
cout << "网络的最小花费是: " << best_expaflo(1, vertex_count) << endl;
cout << "网络的最大流是:" << max_flow << endl;
Print(vertex_count);//输出最后邻接表
Print_flow(vertex_count);
return 0;
}
int Search_flow(int vertex_count)
{
int i, temp,cur;
queue<int> q;//创建队列
/*初始化*/
memset(visited, 0, sizeof(visited));
memset(pre_choice, -1, sizeof(pre_choice));
memset(visited_count, 0, sizeof(visited_count));
for (i = 1; i <= vertex_count; i++)
expense_choice[i] = INF;
/*首节点处理*/
visited[1] = 1;
visited_count[1] ++;
expense_choice[1] = 0;
q.push(1);
while (!q.empty())//队列为空时停止
{
temp = q.front();//出队首元素
q.pop();
visited[temp] = 0;//访问数组复原
for (i = vertex[temp].first; i != -1; i = edge[i].next)//与temp点相连的边的分析
{
cur = edge[i].start; //弧头,如u-->v,其中v叫弧头,u叫弧尾
if (edge[i].contain > edge[i].flow && expense_choice[cur] > expense_choice[temp] + edge[i].expense)//存在可行解
{
expense_choice[cur] = expense_choice[temp] + edge[i].expense;//更新到cur点的花费值
pre_choice[cur] = i;//cur的前驱更新
if (visited[cur] == 0)
{
visited_count[cur]++;//访问次数增加一
q.push(cur);//入队
visited[cur] = 1;//标记
if (visited_count[cur] > vertex_count)//存在负环
return 0;
}
}
}
}
/*输出每次查找到的最优解*/
cout << "最短路数组:" << endl;
cout << " expense_choice [ ] = ";
for (i = 1; i <= vertex_count; i++)
cout << " " << expense_choice[i];
cout << endl;
if (expense_choice[vertex_count] == INF)//到目的地的花费为无穷,即表示无法达到目的地,则结束整个程序
return 0;
return 1;
}
int best_expaflo(int i, int vertex_count)
{
int current_cost,min_cost = 0;//当前花费,最小花费
while (Search_flow(vertex_count))//存在解时,继续搜索
{
current_cost = INF;
cout << endl;
cout << "增广路径:" << vertex_count;
for (i = pre_choice[vertex_count]; i != -1; i = pre_choice[edge[i ^ 1].start])//使用i^1的目的是因为我们在存储的时候采取的是正向为i,那么逆向是i+1
{
current_cost = min(current_cost, edge[i].contain - edge[i].flow);
cout << "--" << edge[i ^ 1].start;
}
cout << "增流:" << current_cost << endl << endl;
max_flow += current_cost;
for (i = pre_choice[vertex_count]; i != -1; i = pre_choice[edge[i ^ 1].start])
{
edge[i].flow += current_cost;//正向增加
edge[i ^ 1].flow -= current_cost;//反向减少
}
min_cost += expense_choice[vertex_count] * current_cost;//计算当前流去汇点的费用
}
return min_cost;
}
void Print(int vertex_count)//输出初始邻接表
{
cout << "网络连接表如下:" << endl;
for (int i = 1; i <= vertex_count; i++)
{
cout << "v" << i << " [" << vertex[i].first;
for (int j = vertex[i].first; j != -1; j = edge[j].next)
cout << "] -- [" << edge[j].start << " " << edge[j].contain << " " << edge[j].flow << " " << edge[j].expense << " " << edge[j].next;
cout << "]" << endl;
}
cout << endl;
return ;
}
void Print_flow(int vertex_count)
{
cout << "实流边如下:" << endl;
for(int i = 1;i <= vertex_count;i++)
for (int j = vertex[i].first; j != -1; j = edge[j].next)
{
if (edge[j].flow > 0)
{
cout << "v" << i << "--" << "v" << edge[j].start << " " << edge[j].flow << " " << edge[j].expense;
cout << endl;
}
}
return;
}
void add_edge(int v, int w, int we, int e)//加边,赋值
{
edge[num].start = w;//弧头
edge[num].contain = we;//容量
edge[num].expense = e;//花费
edge[num].flow = 0;//流量
edge[num].next = vertex[v].first;//下一条边序号
vertex[v].first = num ++;//边的序号编号
return ;
}
void add(int v, int w,int we,int e)//因为是网络混合图,即双向流动,所以两者都需定义
{
add_edge(v, w, we, e);//正向,价格为正
add_edge(w, v, 0, -e);//逆向,价格为负
return ;
}
6
10
1 3 4 7
1 2 3 1
2 5 4 5
2 4 6 4
2 3 1 1
3 5 3 6
3 4 5 3
4 6 7 6
5 6 3 2
5 4 3 3
实现借鉴《趣学算法》