ZOJ 2676 Network Wars ★(最小割算法介绍 && 01分数规划)

题意】给出一个带权无向图,求割集,且割集的平均边权最小。 【 分析】 先尝试着用更一般的形式重新叙述本问题。设向量 w表示边的权值,令向量c=(1, 1, 1, ……, 1)表示选边的代价,于是原问题等价为: Minimize   λ = f(x) = sigma(w ex e)/sigma(1*x e) = wx / cx 其中, x表示一个解向量,x e∈{0, 1} ,即对于每条边都有选与不选两种决策,并且选出的边集组成一个s-t边割集。 联系已有的知识,这是一个0-1分数规划。在 胡伯涛《最小割模型在信息学竞赛中的应用》中已经给出了这类规划的普遍转化方法:构造一个新函数g(λ) = min {( wc)• x} 即对每条边∨e ∈ E ,进行重赋权:w e' = w e - c e•λ = we - λ。g(λ)便是在这个重新赋权的图上,求一个最小容量的s-t边割集。注意一些细节:若w e' < 0,又由于目标函数是加和取最小的操作,则该边必然是在边割集内。对于剩下的所有w e' > 0的边,直接利用最小割模型求出s-t割即可。 于是主算法便是对最优解*λ的二分查找,每次查找用最小割模型来判定,进而缩小查找范围,直到满足精度要求。 【 最小割算法:割点集、割边集】 通常我们说的最小割都是 最小边割,对应求的也就是最小边割集。如果要求 最小点割集,则 拆点转换为边割:( 每个点i拆成i和i',连一条(i, i', 1)的边,原图中的边(u,v)转化为(u', v, oo),求源点s+n到汇点t的最大流)就行了。 由于最大流最小割定理中,即最大流的流值等于最小割容量。所以在问题的实现上分两步: (Δ)先求得最大流,再在得到最大流f后的残留网络Gf中,从s开始深度优先遍历(DFS),所有被遍历到的点,即构成点集S。其余的点即构成点集T(不需要再从T遍历,挺麻烦)。这样 割点集便求出来了。 注意,虽然 最小割[S,T]中的边都是满流边,但 满流边不一定都是最小割中的边。在某些特殊图中,很人容易认为不用DFS,就可以直接得出割。下面举一个二分图的例子。   图1.2(a) 给出了一个基于二分图构造的流网络。由于从X部到Y部都是容量均为正无限的边,都不可能是最小割中的边,有人便会错误地认为与源或汇关联的满流边便组成了最小割(图1.2(a)的红色边)。然而实际上,在该网络的残留网络中,结点2与3应该与源s是连通的(图1.2(b)的蓝色路径),所以最小割应该是图1.2(b)中的红色边。 所以 割边集的求法应该是: 先求出割点集,然后枚举满流边,如果边的两个端点分别在S集和T集中,则该边是割边。   【 代码】  
#include 
 
   
    
  
#include 
  
    
      #include 
     
       #include 
      
        #include 
       
         #include 
        
          #define MID(x,y) ((x+y)/2) #define mem(a,b) memset(a,b,sizeof(a)) using namespace std; const int MAXV = 105; const int MAXE = 1005; const int oo = 0x3fffffff; const double eps = 1e-2; bool dy(double x,double y) { return x > y + eps;} // x > y bool xy(double x,double y) { return x < y - eps;} // x < y bool dyd(double x,double y) { return x > y - eps;} // x >= y bool xyd(double x,double y) { return x < y + eps;} // x <= y bool dd(double x,double y) { return fabs( x - y ) < eps;} // x == y template 
         
           struct Dinic{ struct node{ int u, v; T flow; int opp; int next; }arc[2*MAXE]; int vn, en, head[MAXV]; int cur[MAXV]; int q[MAXV]; int path[2*MAXE], top; int dep[MAXV]; void init(int n){ vn = n; en = 0; mem(head, -1); } void insert_flow(int u, int v, T flow){ arc[en].u = u; arc[en].v = v; arc[en].flow = flow; arc[en].next = head[u]; head[u] = en ++; arc[en].u = v; arc[en].v = u; arc[en].flow = 0; arc[en].next = head[v]; head[v] = en ++; } bool bfs(int s, int t){ mem(dep, -1); int lq = 0, rq = 1; dep[s] = 0; q[lq] = s; while(lq < rq){ int u = q[lq ++]; if (u == t){ return true; } for (int i = head[u]; i != -1; i = arc[i].next){ int v = arc[i].v; if (dep[v] == -1 && arc[i].flow > 0){ dep[v] = dep[u] + 1; q[rq ++] = v; } } } return false; } T solve(int s, int t){ T maxflow = 0; while(bfs(s, t)){ int i, j; for (i = 1; i <= vn; i ++) cur[i] = head[i]; for (i = s, top = 0;;){ if (i == t){ int mink; T minflow = 0x3fffffff; for (int k = 0; k < top; k ++) if (minflow > arc[path[k]].flow){ minflow = arc[path[k]].flow; mink = k; } for (int k = 0; k < top; k ++) arc[path[k]].flow -= minflow, arc[path[k]^1].flow += minflow; maxflow += minflow; top = mink; i = arc[path[top]].u; } for (j = cur[i]; j != -1; cur[i] = j = arc[j].next){ int v = arc[j].v; if (arc[j].flow && dep[v] == dep[i] + 1) break; } if (j != -1){ path[top ++] = j; i = arc[j].v; } else{ if (top == 0) break; dep[i] = -1; i = arc[path[-- top]].u; } } } return maxflow; } }; Dinic 
          
            dinic; int n, m; struct path{ int u, v; double cost; }p[MAXE]; int st[MAXV]; bool vis[MAXV]; void dfs(int u, int p){ st[u] = p; vis[u] = 1; for (int i = dinic.head[u]; i != -1; i = dinic.arc[i].next){ if (dinic.arc[i].flow <= 0) continue; int v = dinic.arc[i].v; if (!vis[v]){ dfs(v, p); } } return ; } double min_cut(double r){ double res = 0.0; dinic.init(n); for (int i = 0; i < m; i ++){ if (p[i].cost - r < 0){ res += p[i].cost - r; } else{ dinic.insert_flow(p[i].u, p[i].v, p[i].cost - r); dinic.insert_flow(p[i].v, p[i].u, p[i].cost - r); } } res += dinic.solve(1, n); return res; } int main(){ //freopen("test.in", "r", stdin); //freopen("test.out", "w", stdout); int ca = 1; while(scanf("%d %d", &n, &m) != EOF){ if (ca > 1) printf("\n"); double max_cost = 0.0; for (int i = 0; i < m; i ++){ scanf("%d %d %lf", &p[i].u, &p[i].v, &p[i].cost); max_cost = max(max_cost, p[i].cost); } double l = 0, r = max_cost; while(xy(l, r)){ double mid = MID(l, r); double cut = min_cut(mid); if (xy(cut, 0)){ r = mid; } else{ l = mid; } } mem(st, 0); mem(vis, 0); dfs(1, 1); int counts = 0; vector 
           
             cuts; cuts.clear(); for (int i = 0; i < m; i ++){ if (p[i].cost - r < 0 || st[p[i].u] != st[p[i].v]){ cuts.push_back(i+1); counts ++; } } printf("%d\n", counts); for (int i = 0; i < counts; i ++){ if (i == 0) printf("%d", cuts[i]); else printf(" %d", cuts[i]); } printf("\n"); ca ++; } return 0; } 
            
           
          
         
        
       
      
    
 
   

你可能感兴趣的:(NetWork)