<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;
}
邻接表--建图、spfa、EK算法 (转化为最小费用最大流)
最新推荐文章于 2022-03-28 21:50:02 发布