邻接表--建图、spfa、EK算法 (转化为最小费用最大流)

<pre name="code" class="cpp">#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <string.h>
#include <queue>
#include <vector>         
#define inf 1<<30     //poj2135(邻接表--建图、spfa、EK算法)      
#define N 1010
using namespace std;
/*
总结:
邻接表相对于邻接矩阵要好得多,一方面它不用考虑是否会重边,因为在下面的操作下会在重边的情况下取得最佳的结果,另一方面它相对于邻接矩阵要节省空间
另外,要注意邻接表的数组要开大点
*/
struct Edge
{
	int u, v, w, cap, next;    //使用邻接表,u表示起点,v表示终点,w表示费用(相当于最短路),cap表示容量
}edge[50010];   //注意,邻接表的数组要开大点
int n, m, index, pre[N], dis[N], vis[N], p[N];
queue<int> q;
void add(int a, int b, int k, int c)
{
	edge[index].u=a;
	edge[index].v=b;
	edge[index].w=c;
	edge[index].cap=k;
	edge[index].next=pre[a];
	pre[a]=index++;
	edge[index].u=b;
	edge[index].v=a;
	edge[index].w=-c;  //注意,同条线段相反方向权值为-c,容量为0
	edge[index].cap=0;
	edge[index].next=pre[b];
	pre[b]=index++;
	return ;
}	
void init()  //构图  
{
	int a, b, c;
	memset(pre, -1, sizeof(pre));
	while(m--)
	{
		scanf("%d%d%d", &a, &b, &c);//注意这里的线段是双向的
		add(a, b, 1, c);   //一条线段就要上面的两个操作
		add(b, a, 1, c); 
	}
	add(0, 1, 2, 0); //以下建立超级源点0和超级汇点n+1,其他点从1开始
	add(n, n+1, 2, 0);
	return ;
}
int mpmf()
{
	int t, s=0, flow, totprice=0, g=n+1, u;
	while(!q.empty())
		q.pop();	
	while(1)    //spfa算法
	{
		for(t=0; t<=g; ++t)
		{
			dis[t]=inf;
			vis[t]=0;
		}
		dis[s]=0;    //注意起点不能改变
		vis[s]=1;
		q.push(s);
		while(!q.empty())
		{
			u=q.front();
			q.pop();
			vis[u]=0;
			for(t=pre[u]; t!=-1; t=edge[t].next)
			{
				if(edge[t].cap&&dis[edge[t].v]>dis[u]+edge[t].w)
				{
					dis[edge[t].v]=dis[u]+edge[t].w;
					p[edge[t].v]=t;  //这里记录的方法和邻接矩阵不同,记录的是尾点在edge数组的位置
					if(!vis[edge[t].v])
					{
						vis[edge[t].v]=1;
						q.push(edge[t].v);
					}
				}
			}
		}
		if(dis[g]==inf)
			break;
		for(t=g, flow=inf; t!=s; t=edge[p[t]].u)  //取线路中的最小流量
		{
			if(edge[p[t]].cap<flow)
				flow=edge[p[t]].cap;  
		}
		for(t=g; t!=s; t=edge[p[t]].u)   //更新流量
		{
			edge[p[t]].cap-=flow;
			edge[p[t]^1].cap+=flow;
		}
		totprice+=dis[g]*flow;  //最小费用最大流
	}
	return totprice;
}

int main()
{
	while(scanf("%d%d", &n, &m)!=EOF)
	{
		index=0;
		init();  //初始化邻接表
		printf("%d\n", mpmf());
	}
	return 0;
}


 

                
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值