poj 1459 power network(网络流 di…

题目大意:
       给几个发电站,给几个消耗站,再给几个转发点。
       发电站只发电,消耗站只消耗电,转发点只是转发电,再给各个传送线的传电能力。
       问你消耗站能获得的最多电是多少

思路:建立一个超级源点和超级汇点,把发电站相连起来,消耗站连起来 然后就是模版的力量了

在此也讲了下dinic的原理:
求最大流的本质,就是不停的寻找增广路径。直到找不到增广路径为止。
对于这个一般性的过程,Dinic算法的优化如下:

(1)
Dinic算法首先对图进行一次BFS,然后在BFS生成的层次图中进行多次DFS。
层次图的意思就是,只有在BFS树中深度相差1的节点才是连接的。
这就切断了原有的图中的许多不必要的连接。很牛逼!
这是需要证明的,估计证明也很复杂。

(2)
除此之外,每次DFS完后,会找到路径中容量最小的一条边。
在这条边之前的路径的容量是大于等于这条边的容量的。
那么从这条边之前的点,可能引发出别的增广路径。
比如说  S -> b -> c -> d -> T 是一条增广路径,容量最小的边是 b -> c。
可能存在一条 S -> b -> e -> f -> g -> T 这样的增广路径。
这样的话,在找到第一条增广路径后,只需要回溯到 b 点,就可以继续找下去了。
这样做的好处是,避免了找到一条路径就从头开始寻找另外一条的开销。
也就是再次从 S 寻找到 b 的开销。
这个过程看似复杂,但是代码实现起来很优雅,因为它的本质就是回溯!
(3)
在同一次 DFS 中。如果从一个点引发不出任何的增广路径,就将这个点在层次图中抹去。


#include <stdio.h>
#include <string.h>
#define VM 150
#define EM 20550
#define inf 0x3f3f3f3f
struct edge
{
      int frm,to,cap,next;
}e[EM];

int head[VM],dep[VM],ep,n;        //dep为点的层次
void addedge (int cu,int cv,int cw)  //第一条边下标必须为偶数
{
      e[ep].frm = cu;
      e[ep].to = cv;
      e[ep].cap = cw;
      e[ep].next = head[cu];
      head[cu] = ep;
      ep ++;
      e[ep].frm = cv;
      e[ep].to = cu;
      e[ep].cap = 0;
      e[ep].next = head[cv];
      head[cv] = ep;
      ep ++;
}

int BFS (int src,int des)        //求出层次图
{
      int que[VM],i,front = 0,rear = 0;
      memset (dep,-1,sizeof(dep));
      que[rear++] = src;
      dep[src] = 0;
      while (front != rear)
      {
              int u = que[front++];
              front = front%VM;
              for (i = head[u];i != -1;i = e[i].next)
              {
                      int v = e[i].to;
                      if (e[i].cap > 0&&dep[v] == -1) //容量大于0&&未在dep中
                      {
                              dep[v] = dep[u] + 1;              //建立层次图
                              que[rear ++] = v;
                              rear = rear % VM;
                              if (v == des)  //找到汇点 返回
                                      return 1;
                      }
              }
      }
      return 0;
}
int dinic (int src,int des)
{
      int i,res = 0,top;
      int stack[VM];      //stack为栈,存储当前增广路
      int cur[VM];              //存储当前点的后继 跟head是一样的
      while (BFS(src,des))    //if BFS找到增广路
      {
              memcpy (cur,head,sizeof (head));
              int u = src;            //u为当前结点
              top = 0;
              while (1)
              {
                      if (u == des)        //增广路已全部进栈
                      {
                              int min = inf,loc ;
                              for (i = 0;i < top;i ++)            //找最小的增广跟并loc记录其在stack中位置
                                      if (min > e[stack[i]].cap)  //以便退回该边继续DFS
                                      {
                                              min = e[stack[i]].cap;
                                              loc = i;
                                      }
                              for (i = 0;i < top;i ++)    //偶数^1 相当加1 奇数^1相当减1 当正向边 = 0&&路径不合适时,正加负减
                                                                                //偶数是正向边,奇数是负向边,边从0开始
                                      e[stack[i]].cap -= min;
                                      e[stack[i]^1].cap += min;
                                                                                      //将增广路中的所有边修改
                              res += min;
                              top = loc;
                              u = e[stack[top]].frm;                //当前结点修改为最小边的起点
                      }
                      for (i = cur[u];i != -1;cur[u] = i = e[i].next)    //找到当前结点对应的下一条边
                              if (e[i].cap != 0&&dep[u] + 1 == dep[e[i].to])//不满足条件时,修改cur值(去掉不合适的占)eg:1-->2 1-->3 1-->4 有边 但只有
                                      break;                                                                  // 1-->4 这条边满足条件 就把1到2、3的边给去掉
                      if (cur[u] != -1)                      //当前结点的下一条边存在
                      {
                              stack[top ++] = cur[u];    //把该边放入栈中
                              u = e[cur[u]].to;                //再从下个点开始找
                      }
                      else
                      {
                              if (top == 0)              //当前结点无未遍历的下一条边且栈空,DFS找不到下一条增广路
                                      break;
                              dep[u] = -1;                      //当前结点不在增广路中,剔除该点
                              u = e[stack[--top]].frm; //退栈 回朔,继续查找
                      }
              }
      }
      return res;
}
int main ()
{
      int np,nc,m,v1,v2,w;
      int src,des;
      char str[20];
      while (scanf ("%d%d%d%d",&n,&np,&nc,&m)!=EOF)
      {
              ep = 0;
              src = n;
              des = n+1;
              memset (head,-1,sizeof(head));
              while (m --)
              {
                      scanf ("%s",str);
                      sscanf (str,"(%d,%d)%d",&v1,&v2,&w);
                      addedge (v1,v2,w);
              }
              while (np --)
              {
                      scanf ("%s",str);
                      sscanf (str,"(%d)%d",&v2,&w);
                      addedge (src,v2,w);
              }
              while (nc--)
              {
                      scanf ("%s",str);
                      sscanf (str,"(%d)%d",&v1,&w);
                      addedge (v1,des,w);
              }
              int ans = dinic (src,des);
              printf ("%d\n",ans);
      }
      return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值