最小费用最大流 + EK最大流

上周末打了两场网络赛,对于网络流的应用有了更深的体会【简单来讲就是还不懂得怎么自己写,就只是好像懂了一丢丢要怎么用】。把kuangbin大佬的模板再手敲一遍加上自己的注释。

//最小费用最大流

#include
#include
#include
#include
#include
#include
using namespace std;


const int maxn = 1e4;
const int maxm = 1e5;
const int inf = 0x3f3f3f3f;
struct Edge
{
    int to,next,cap,flow,cost;
}edge[maxm];
int head[maxn],tol;
int pre[maxn],dis[maxn];
bool vis[maxn];
int N; //节点个数,编号0->N-1 !全局变量 需要init赋值或主函数改变

void init(int n)
{
    N=n;
    tol = 0;
    memset(head,-1,sizeof(head));
}

void addedge(int u,int v,int cap,int cost) //边起点,终点,流量,费用
{
    edge[tol].to = v;
    edge[tol].cap = cap;
    edge[tol].cost = cost;
    edge[tol].flow = 0;
    edge[tol].next = head[u];
    head[u] = tol++;
    edge[tol].to = u;
    edge[tol].cap = 0;
    edge[tol].cost = -cost;
    edge[tol].flow = 0;
    edge[tol].next = head[v];
    head[v] = tol++;
}

bool spfa(int s,int t)     //单源最短路径算法 可判断负环
{
    queue<int >q;
    for(int i=0;ifalse;
        pre[i] = -1;
    }
    dis[s] = 0;
    vis[s] = true;
    q.push(s);
    while(!q.empty())
    {
        int u = q.front();
        q.pop();
        vis[u] = false;
        for(int i=head[u];i!=-1;i=edge[i].next)
        {
            int v= edge[i].to;
            if(edge[i].cap>edge[i].flow && dis[v]>dis[u]+edge[i].cost)
            {
                dis[v] = dis[u] + edge[i].cost;
                pre[v] = i;
                if(!vis[v])
                {
                    vis[v] = true;
                    q.push(v);
                }
            }
        }
    }
    if(pre[t]==-1) return false;
    else return true;
}

int MCMF(int s,int t,int &cost)  //MinCostMaxFlow  返回最大流,cost存最小费用
{
    int flow = 0;
    cost = 0;
    while(spfa(s,t))
    {
        int Min = inf;
        for(int i= pre[t];i!=-1;i=pre[edge[i^1].to])
        {
            if(Min>edge[i].cap-edge[i].flow)
                Min=edge[i].cap-edge[i].flow;
        }
        for(int i= pre[t];i!=-1;i=pre[edge[i^1].to])
        {
            edge[i].flow += Min;
            edge[i^1].flow -=Min;
            cost += edge[i].cost*Min;
        }
        flow += Min;
    }
    return flow;
}

最小费用最大流属于网络流的一种基本算法,上周末的应用中,一道是滑雪求最长路径(滑雪只能从高到低,所以是有向路),一道是在n个城市中辗转买、卖书的问题(中间没有多次买卖,choose two cities to buy and sell)。
两道题的共同特点是n个点相互之间有路,不管是不是有向的,然后我们要找到最符合要求的一种方式。两道题中我们都可以额外添加两个点作为入点和汇点,用流量来限制通过每条路、每个点的次数,用费用来体现两点之间的【贡献】。
在滑雪的问题中,因为题目中要求每个点只能经过一次(流量限制的是每条路经过的次数),所以我们可以把每个点拆成两个点所连通的路,并且把这条路的流量设置为1。
两道题中,所求的其实是最大费用最大流的问题(滑雪路径最长,赚的钱最多)。我们可以将每条路的花费设置成负数,就可以了。有向边和无向边的区别我们可以建两个有向边。

// EK 最大流

#include
#include
#include
#include
#include
using namespace std;

const int MAXN=500;
const int INF=0x3fffffff;
int g[MAXN][MAXN];//存边的容量,没有边的初始化为0
int path[MAXN], flow[MAXN], S, T;
int n;//点的个数,编号0-n.n包括了源点和汇点。

queue<int>q;
int bfs()
{
    int i,t;
    while(!q.empty())q.pop();//把清空队列
    memset(path,-1,sizeof(path));//每次搜索前都把路径初始化成-1
    path[S]=0;
    flow[S]=INF;//源点可以有无穷的流流进
    q.push(S);
    while(!q.empty())
    {
        t=q.front();
        q.pop();
        if(t==T)break;
        //枚举所有的点,如果点的编号起始点有变化可以改这里
        for(i=0;i<=n;i++)
        {
            if(i!=S&&path[i]==-1&&g[t][i])
            {
                flow[i]=flow[t]if(path[T]==-1)return -1;
    return flow[T];
}
int Edmonds_Karp()
{
    int max_flow=0;
    int step, now, pre;
    while((step=bfs()) != -1)
    {
        max_flow += step;
        now = T;
        while(now != S)
        {
            pre = path[now];
            g[pre][now] -= step;
            g[now][pre] += step;
            now = pre;
        }
    }
    return max_flow;
}
int main()
{
    // g[u][v] = w;

    return 0;
}

End E n d

你可能感兴趣的:(模板,图论)