UVA - 10806(最大流最小费用)模版+添加虚拟点

 https://uva.onlinejudge.org/external/108/10806.pdf

终于可以写这道题的题解了,昨天下午纠结我一下下午,晚上才照着人家的题解敲出来,今天上午又干坐着想了两个小时,才弄明白这个问题。

题意很简单,给出一个无向图,要求从1 到 n最短路两次,但是两次不允许经过同一条边(正反都不能经过),如果能成功,则输出两次的最小长度。否则输出一句话。

我当时就马上敲了一个最短路,执行两次,在第一次执行完之后就把所经过的路径的正反都锁定好,不允许下次再访问。。。这样做通过了sample,但是WA了,我后来上网查,原来好多人是我这种做法,。。而且原来这是最小费用最大流问题。

分析一下为什么我的做法不行,因为这道题目要求在能通过的前提下,最短,也就是说,我连续两次最短路,如果第二次不能通过,就放弃,这明显是错的,最终的结果,也许是一条最短路+一条长路,或者两条都不是最短路。。。

所以一种可行的修改方案是,在原来的代码基础上,不是直接封锁正反两条路,而是封锁正向,把反向边=权值的相反数,为什么这样的,简而言之,就是给出反悔的机会

我们来思考这样一种实际的例子 如果 path1:起点-p1-A-B-p2-终点; path2:起点-p3-B-A-P4-终点;p1 p2 p3 p4都是互不干扰的路径,这样的话,其实两条路径就是p1-p3

p2-p4,A-B作为公共边,在程序里面最短路确实是这样通过的,但实际上由于权值取反,正好抵消,使得A-B B-A实际意义上是没有被经过,但是路径是通的,这样就能够智能的去选择路径方案。

 

最终我是用最小费用最大流来解决的,通过这个题目,我对最大流有了更深刻的理解,明白了残量图的更大的作用,为什么当时E-K算法里面,找到增广路后需要对正向流进行扩展,同时又对反向流减少,这其实就是对应了现实的流的概念,我递增正向,但是流是守恒的,我可以通过反向把流又送回来,因此用这个方法,使得最大流不会放过任何一个能走通的路径,在E-K算法里,其实流经过了多次往返,只有确定无法走通,才会放弃。。。同时在建图的时候,因为是无向图,所以每条边,都要建立一个相应的负费用边。。。这样的话每个端点实际上连了两条边,一个是自己的正费用边,一个是对面端点的负费用边,我一开始会觉得这样在最短路过程中会不会发生混乱,即这个点往下走,到底是走的自己本身的正费用边,还是负费用边,后来不管费用如何,最短路最终会选择最优的那条,因此不会有问题

一条无向边对应2条有向的 每条还有一条相反的 cap = 0 cost = -w的反向边 总共4条 

#include 
#include 
#include 
#include 
#include 
#include
using namespace std;
const int maxn = 2000 + 10;
const int INF = 1000000;

struct Edge {
  int from, to, cap, flow, cost;
  Edge(int u, int v, int c, int f, int w):from(u),to(v),cap(c),flow(f),cost(w) {}
};

struct MCMF {
  int n, m;
  vector edges;
  vector G[maxn];
  int inq[maxn];         // 是否在队列中
  int d[maxn];           // Bellman-Ford
  int p[maxn];           // 上一条弧
  int a[maxn];           // 可改进量

  void init(int n) {
    this->n = n;
    for(int i = 0; i < n; i++) G[i].clear();
    edges.clear();
  }

  void AddEdge(int from, int to, int cap, int cost) {
    edges.push_back(Edge(from, to, cap, 0, cost));
    edges.push_back(Edge(to, from, 0, 0, -cost));
    m = edges.size();
    G[from].push_back(m-2);
    G[to].push_back(m-1);
  }

  bool BellmanFord(int s, int t, int flow_limit, int& flow, int& cost) {
 
    for(int i = 0; i < n; i++) d[i] = INF;
    memset(inq, 0, sizeof(inq));
    d[s] = 0; inq[s] = 1; p[s] = 0; a[s] = INF;

    queue Q;
    Q.push(s);
    while(!Q.empty()) {
      int u = Q.front(); Q.pop();
      inq[u] = 0;
      for(int i = 0; i < G[u].size(); i++) {
        Edge& e = edges[G[u][i]];
        if(e.cap > e.flow && d[e.to] > d[u] + e.cost) {
          d[e.to] = d[u] + e.cost;
          p[e.to] = G[u][i];
          a[e.to] = min(a[u], e.cap - e.flow);
          if(!inq[e.to]) { Q.push(e.to); inq[e.to] = 1; }
        }
      }
    }
    if(d[t] == INF) return false;
    if(flow + a[t] > flow_limit) a[t] = flow_limit - flow;
    flow += a[t];
    cost += d[t] * a[t];
    for(int u = t; u != s; u = edges[p[u]].from) {
      edges[p[u]].flow += a[t];
      edges[p[u]^1].flow -= a[t];
    }

    return true;
  }

  // 需要保证初始网络中没有负权圈
  int MincostFlow(int s, int t, int flow_limit, int& cost) {

    int flow = 0; cost = 0;
    while(flow < flow_limit && BellmanFord(s, t, flow_limit, flow, cost));
    return flow;
  }

};
MCMF mcmf;

int main() {
	
	int n,m,icount=1;
	int s,t;
    while(scanf("%d", &n) == 1 && n) 
	{
		mcmf.init(n+1);
		s=0;
		t=n;
		mcmf.AddEdge(0, 1, 2, 0);
		scanf("%d", &m);
		for(int i=0;i

 

你可能感兴趣的:(mfmc,程序片段)