POJ 1459--Power Network(三种方法求最大流)

32 篇文章 0 订阅
24 篇文章 0 订阅

  • 根据题意,新建一个超级节点,所有发电站的流量由此节点发出,新建一个超级汇点,所有节点 使用的流量都流到此节点。 然后采用最大流的各种方法求解。
  1. 不管用ford——fulkerson方法还是推送重贴标签,首先都不用管自环,显然一个最大流是不会包括自环的,自环只会减少流量。然后反向边也不需要如算法导论中加入另外的点进行处理。为了减少内存,直接用容量的减少代表流量的增加,正向边容量减少多少,正向流 量就增加多少,同时增加相应的反向容量,意思就是说反向的能允许的流量增加,而如果接下来的操作反向容量减少,也就是有反向的流量,也即是正向流量的减少,不管正向反向流量都可增可少,但是注意的是实际上最大流只会有一个方向的流量,但是可以等效为上述操作。直至所有增广路径的残余流量都是0。-------------ford——fulkerson
  2. push——relabel:此算法较难理解,初始化后若一个点有溢出,并且其所有能流入的点(因为在构图里面允许反向边,所以不用像算法导论里面允许一个反向的流量作为残余边 来减少流量,而本算法在对反向容量增加时可以达到同样的效果。所以只要capacity[u][v]>0就说明还有残余流量)高度均大于等于它,就对此点更新高度。若此点高度等于其能流入的点高度加1,则对此边推入操作。因为在满足溢出,可流入条件下,推入和重贴标签是互斥的,可以优化。
  3. Dinic:  首先BFS分层,为了构造一个有向无环图,当BFS无法到达汇点时,已达最大流。然后对上图作DFS,类似于二分匹配,当DFS到最后一点,其不能向下迭代但是又不是汇点则将此点标记不再走。当到达汇点回溯时,只回溯到路径最小边(u,v),因为显然从u点还可能向下找增广路径,可继续使用u点以前多余的流量,所以要对每点设置一个总增广值sumAug,记录此点能到达的所有增广路径使用的流量,然后再从此点回溯。

ford-fulkerson:
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
#define min(x,y) (x>y?y:x)
#define INF   (0X7F7F)
#define maxN  105

short capacity[maxN][maxN];
short nodeNum;
short start;
short dest;

int maxFlow()
{
    short fa[maxN];
    short minPathFlow[maxN];
    queue<short> q;
    short now,i,tmpFlow,son;
    int maxFlow = 0;
    while(1)
    {
        memset(fa,127,sizeof(short)*(nodeNum+2));
        memset(minPathFlow,127,sizeof(short)*(nodeNum+2));
        q.push(start);
        while(!q.empty())              //寻找始终点最短路径
        {
            now = q.front();
            q.pop();
            for(i = 0;i <= nodeNum;i++)
            {
                tmpFlow = capacity[now][i];
                if(fa[i] == INF&&tmpFlow > 0)
                {
                if(i == dest)
                {
                    minPathFlow[dest] = min(minPathFlow[now],tmpFlow);
                    fa[dest] = now;           //并查集
                    break;
                }
                else
                {
                    fa[i] = now;
                    minPathFlow[i] = min(minPathFlow[now],tmpFlow);
                    q.push(i);
                }
                }
            }
        }
        if(minPathFlow[dest] != INF)          //更新流量
        {
            maxFlow += minPathFlow[dest];
            son = dest;
            while(son != start)
            {
                capacity[fa[son]][son] -= minPathFlow[dest];
                capacity[son][fa[son]] += minPathFlow[dest];
                son = fa[son];
            }
        }
        else
            break;
    }
    cout<<maxFlow<<endl;
    return 0;
}

int main()
{
    char tmpCh;
    short u,v,cmax,powerNum,consumerNum,lineNum;
    while(scanf("%hd%hd%hd%hd",&nodeNum,&powerNum,&consumerNum,&lineNum)!=EOF)
    {
        memset(capacity,0,sizeof(short)*(maxN*maxN));
        while(lineNum--)
        {
            cin>>tmpCh>>u>>tmpCh>>v>>tmpCh>>cmax;
                capacity[u][v] = cmax;
        }
        start = nodeNum+1;
        dest = nodeNum;
        while(powerNum--)
        {
            cin>>tmpCh>>u>>tmpCh>>cmax;
            capacity[start][u] = cmax;
        }
        while(consumerNum--)
        {
            cin>>tmpCh>>u>>tmpCh>>cmax;
            capacity[u][dest] = cmax;
        }
        maxFlow();
    }
    return 0;
}

push-relabel to front:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define maxN 105
#define min(x,y)   (x>y?y:x)
#define INF  (0x7F7F)

int capacity[maxN][maxN];
short nodeNum;
short start,dest;
short height[maxN];
int exceFlow[maxN];

struct node
{
    short x;
    node *next;
    node *prev;
}*head;

bool push(short u,short v)
{
    int minFlow = min(exceFlow[u],capacity[u][v]);
    capacity[u][v] -= minFlow;
    capacity[v][u] += minFlow;
    exceFlow[u] -= minFlow;
    exceFlow[v] += minFlow;
    return true;
}

bool relabel(short u)
{
    short i;
    short minHeight = INF;
    for(i = 0;i < nodeNum;i++)
    {
        if(capacity[u][i] > 0&&height[u] <= height[i])
            minHeight = min(minHeight,height[i]);
    }
    height[u] = minHeight+1;
    return true;
}

int discharge(short u)
{
    short v = 0;
    while(exceFlow[u] > 0)
    {
        if(v == nodeNum)
        {
            relabel(u);
            v = 0;
        }
        else if(capacity[u][v] > 0&&height[u] == height[v]+1)
        {
            push(u,v);
        }
        else
        {
            v++;
        }
    }
    return 1;
}

int moveToHead(node *tmp)
{
    if(tmp->prev != NULL)
    {
        if(tmp->next != NULL)
        {
            tmp->prev->next = tmp->next;
            tmp->next->prev = tmp->prev;
        }
        else
        {
            tmp->prev->next = NULL;
        }
        tmp->next = head;
        head->prev = tmp;
        head = tmp;
        head->prev = NULL;
    }
    return 1;
}

int relabelToFront()
{
    short i,u;
    bool IsMax = false;
    short oldHeight;
    bool IsRelabel;
    /*****Initialize preflow******/
    memset(height,0,sizeof(short)*(nodeNum));
    height[start] = nodeNum;
    memset(exceFlow,0,sizeof(int)*(nodeNum));
    for(i = 0;i < nodeNum-2;i++)
    {
        if(capacity[start][i] > 0)
        {
            exceFlow[i] = capacity[start][i];
            exceFlow[start] -= capacity[start][i];
            capacity[i][start] = capacity[start][i];
            capacity[start][i] = 0;
        }
    }
    /*****Init List*********/
    node *tmp;
    head = NULL;
    for(i = 0;i < nodeNum-2;i++)
    {
        tmp = new node;
        tmp->x = i;
        tmp->next = head;
        if(head != NULL)
            head->prev = tmp;
        head = tmp;
        head->prev = NULL;
    }
    tmp = head;
    while(tmp != NULL)
    {
        u = tmp->x;
        oldHeight = height[u];
        discharge(u);
        if(height[u] > oldHeight)
        {
            moveToHead(tmp);
        }
        tmp = tmp->next;
    }
    delete[] tmp;
    delete[] head;
    cout<<exceFlow[dest]<<endl;
    return 1;
}


int main()
{
    short powerNum,consumerNum,lineNum;
    int u,v,cmax;
    char tmpCh;
    while(cin>>nodeNum>>powerNum>>consumerNum>>lineNum)
    {
        memset(capacity,0,sizeof(int)*(maxN*maxN));
        while(lineNum--)
        {
            cin>>tmpCh>>u>>tmpCh>>v>>tmpCh>>cmax;
            if(u != v)
            capacity[u][v] = cmax;
        }
        nodeNum += 2;
        start = nodeNum-1;
        dest = nodeNum-2;
        while(powerNum--)
        {
            cin>>tmpCh>>u>>tmpCh>>cmax;
            capacity[start][u] = cmax;
        }
        while(consumerNum--)
        {
            cin>>tmpCh>>u>>tmpCh>>cmax;
            capacity[u][dest] = cmax;
        }
        relabelToFront();
    }
    return 0;
}

Dinic:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define maxN 105
#define min(x,y)   (x>y?y:x)
#define INF  (0x7F7F)

int capacity[maxN][maxN];
short nodeNum;
short start,dest;
short height[maxN];
int maxFlow;

int Aug(short u,int minFlow)
{
    short i;
    int tmpFlow,tmpAug;
    int sumAug = 0;
    if(u == dest)
    {
        maxFlow += minFlow;
        return minFlow;           //返回增广路径中最小容量,能增加的最大流量
    }
    for(i = 1;i < nodeNum;i++)
    {
        if(capacity[u][i] > 0&&height[i] == height[u]+1)
        {
            tmpFlow = min(minFlow,capacity[u][i]);
            tmpAug = Aug(i,tmpFlow);
            sumAug += tmpAug;               //从u点出发可能有不同的增广路径,将所有路径的容量保存在sumAug
            if(tmpAug)
            {
                capacity[u][i] -= tmpAug;
                capacity[i][u] += tmpAug;
                if(capacity[u][i] != 0)      //若流量减为0,即寻找的增广路径的最小容量在此点,则从此点父节点再往下增广,反之直接返回找到路径容量最小点
                return sumAug;
                else
                {
                    minFlow -= tmpAug;        //再往下增广时,能允许的流量减少
                }
            }
            else
            {
                height[i] = INF;             //从i点已无增广路径,将此点锁定不被访问
            }
        }
    }
    return sumAug;
}

int BFS()
{
    short q[maxN];
    short now,rear,i;
    q[now = rear = 0] = start;        
    memset(height,127,sizeof(short)*(nodeNum));
    height[start] = 0;
    while(now <= rear)
    {
        for(i = 1;i < nodeNum;i++)
        {
            if(capacity[q[now]][i] > 0&&height[i] == INF)
            {
                height[i] = height[q[now]]+1;            //可流的两点高度相差一,高度相差一不一定可流
                q[++rear] = i;
                if(i == dest)
                    return 1;
            }
        }
        now++;
    }
    return 0;
}

int Dinic()
{
    maxFlow = 0;
    while(BFS())
    {
        Aug(start,INF);
    }
    cout<<maxFlow<<endl;
    return 1;
}

int main()
{
    short powerNum,consumerNum,lineNum;
    int u,v,cmax;
    char tmpCh;
    while(cin>>nodeNum>>powerNum>>consumerNum>>lineNum)
    {
        memset(capacity,0,sizeof(int)*(maxN*maxN));
        while(lineNum--)
        {
            cin>>tmpCh>>u>>tmpCh>>v>>tmpCh>>cmax;
            if(u != v)
            capacity[++u][++v] = cmax;
        }
        nodeNum += 2;
        start = 0;
        dest = nodeNum-1;
        while(powerNum--)
        {
            cin>>tmpCh>>u>>tmpCh>>cmax;
            capacity[start][++u] = cmax;
        }
        while(consumerNum--)
        {
            cin>>tmpCh>>u>>tmpCh>>cmax;
            capacity[++u][dest] = cmax;
        }
        Dinic();
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值