《算法》第六章部分程序 part 7

? 书中第六章部分程序,加上自己补充的代码,包括全局最小切分 Stoer-Wagner 算法,最小权值二分图匹配

● 全局最小切分 Stoer-Wagner 算法

  1 package package01;
  2
  3 import edu.princeton.cs.algs4.In;
  4 import edu.princeton.cs.algs4.StdOut;
  5 import edu.princeton.cs.algs4.EdgeWeightedGraph;
  6 import edu.princeton.cs.algs4.Edge;
  7 import edu.princeton.cs.algs4.UF;
  8 import edu.princeton.cs.algs4.IndexMaxPQ;
  9
 10 public class class01
 11 {
 12     private static final double FLOATING_POINT_EPSILON = 1E-11;
 13     private double weight = Double.POSITIVE_INFINITY;           // 输出的最小割值
 14     private boolean[] cut;                                      // 顶点是否在割除集 T 中
 15     private int V;
 16
 17     private class CutPhase                                      // 最小 s-t 割类(cut-of-the-phase)
 18     {
 19         private double weight;
 20         private int s;
 21         private int t;
 22
 23         public CutPhase(double inputWeight, int inputS, int inputT)
 24         {
 25             weight = inputWeight;
 26             s = inputS;
 27             t = inputT;
 28         }
 29     }
 30
 31     public class01(EdgeWeightedGraph G)
 32     {
 33         UF uf = new UF(G.V());                              // 用类 union–find 来表示顶点的合并情况
 34         boolean[] marked = new boolean[G.V()];              // 已合并的顶点集,初始化为空
 35         cut = new boolean[G.V()];                           // 割除集 T,初始化为空
 36         CutPhase cp = new CutPhase(0.0, 0, 0);              // 用于首次搜索的割,无意义
 37         for (int v = G.V(); --v > 0; marked[cp.t] = true)   // 遍历 V-1 次,每次标记被合并的顶点,以后不再遍历该点
 38         {
 39             cp = minCutPhase(G, marked, cp);                // 更新最小割
 40             if (cp.weight < weight)                         // 发现权值更小的割,更新全局割
 41             {
 42                 weight = cp.weight;
 43                 for (int j = 0; j < G.V(); j++)             // 在最新的图中,与 cp.t 相连的顶点都是 T 的元素
 44                     cut[j] = uf.connected(j, cp.t);
 45             }
 46             G = contractEdge(G, cp.s, cp.t);                // 顶点 t 合并到顶点 s,更新图
 47             uf.union(cp.s, cp.t);                           // 顶点 t 加入 T 中
 48         }
 49     }
 50
 51     public double weight()
 52     {
 53         return weight;
 54     }
 55
 56     public boolean cut(int v)
 57     {
 58         return cut[v];
 59     }
 60
 61     private CutPhase minCutPhase(EdgeWeightedGraph G, boolean[] marked, CutPhase cp)    // 计算 s - t 的最小割,称为 maximum adjacency (cardinality) search
 62     {
 63         IndexMaxPQ<Double> pq = new IndexMaxPQ<Double>(G.V());      // 用于挑选权值最大的点用于合并
 64         pq.insert(cp.s, Double.POSITIVE_INFINITY);                  // 顶点 s 自己的权值为 +∞
 65         for (int v = 0; v < G.V(); v++)                             // 其他顶点权值初始化为 0
 66         {
 67             if (v != cp.s && !marked[v])
 68                 pq.insert(v, 0.0);
 69         }
 70         for (; !pq.isEmpty();)
 71         {
 72             cp.s = cp.t;                                            // 记录最后取出的两个顶点,每次往前挪一格
 73             cp.t = pq.delMax();                                     // 取走权值最大的顶点 v
 74             for (Edge e : G.adj(cp.t))                              // 只要 cp.t 的邻居还在 pq 中没有被取走,就更新邻居的权值
 75             {
 76                 int w = e.other(cp.t);
 77                 if (pq.contains(w))
 78                     pq.increaseKey(w, pq.keyOf(w) + e.weight());    // 顶点 w 的权值自增边 e 的权值
 79             }
 80         }
 81         cp.weight = 0.0;                                            // 计算最后加入 T 的顶点的权值,即为最小割值
 82         for (Edge e : G.adj(cp.t))
 83             cp.weight += e.weight();
 84         return cp;
 85     }
 86
 87     private EdgeWeightedGraph contractEdge(EdgeWeightedGraph G, int s, int t)   // 把顶点 t 合并到顶点 s,更新其他边的权值
 88     {
 89         EdgeWeightedGraph H = new EdgeWeightedGraph(G.V());         // 合并后的图,顶点与原来相同
 90         for (int v = 0; v < G.V(); v++)
 91         {
 92             for (Edge e : G.adj(v))                                 // 依顶点序遍历所有边
 93             {
 94                 int w = e.other(v);
 95                 if (v == s && w == t || v == t && w == s)           // 边 s-t 自身不要
 96                     continue;
 97                 if (v < w)                                          // 只考虑后向边,滤掉重复
 98                 {
 99                     if (w == t)                                     // 远端顶点 w 是被合并的 t,边 v - w(t) 替换成边 v - s
100                         H.addEdge(new Edge(v, s, e.weight()));
101                     else if (v == t)                                // 近端顶点 v 是被合并的 t,边 v(t) - w 替换成边 s - w
102                         H.addEdge(new Edge(w, s, e.weight()));
103                     else                                            // 边 v - w 与 s 或 t 无关,原样放进 H
104                         H.addEdge(new Edge(v, w, e.weight()));
105                 }
106             }
107         }
108         return H;
109     }
110
111     public static void main(String[] args)
112     {
113         In in = new In(args[0]);
114         EdgeWeightedGraph G = new EdgeWeightedGraph(in);
115         class01 mc = new class01(G);
116         StdOut.print("Min cut: ");
117         for (int v = 0; v < G.V(); v++)
118         {
119             if (mc.cut(v))
120                 StdOut.print(v + " ");
121         }
122         StdOut.println("\nMin cut weight = " + mc.weight());
123     }
124 }

● 最小权值二分图匹配

  1 package package01;
  2
  3 import edu.princeton.cs.algs4.StdOut;
  4 import edu.princeton.cs.algs4.StdRandom;
  5 import edu.princeton.cs.algs4.EdgeWeightedDigraph;
  6 import edu.princeton.cs.algs4.DirectedEdge;
  7 import edu.princeton.cs.algs4.DijkstraSP;
  8
  9 public class class01
 10 {
 11     private static final double FLOATING_POINT_EPSILON = 1E-14;
 12     private int n;              // 二分图一侧的顶点数,总顶点数2 * n
 13     private double[][] weight;  // 边权矩阵
 14     private double minWeight;   // 最小权值
 15     private double[] px;        // 每一行的对偶变量
 16     private double[] py;        // 每一列的对偶变量
 17     private int[] xy;           // 正向标记,xy[i] = j 表示 i-j 匹配
 18     private int[] yx;           // 反向标记,yx[j] = i 表示 i-j 匹配
 19
 20     public class01(double[][] inputWeight)
 21     {
 22         n = inputWeight.length;
 23         weight = new double[n][n];
 24         minWeight = Double.MAX_VALUE;
 25         for (int i = 0; i < n; i++)
 26         {
 27             for (int j = 0; j < n; j++)
 28             {
 29                 if (Double.isNaN(weight[i][j]))
 30                     throw new IllegalArgumentException("weight " + i + "-" + j + " is NaN");
 31                 weight[i][j] = inputWeight[i][j];
 32                 minWeight = Math.min(minWeight, weight[i][j]);
 33             }
 34         }
 35         px = new double[n];
 36         py = new double[n];
 37         xy = new int[n];
 38         yx = new int[n];
 39         for (int i = 0; i < n; i++)
 40             xy[i] = yx[i] = -1;
 41
 42         for (int k = 0; k < n; k++)             // 调整 n 次,每次添加一条边
 43         {
 44             assert isDualFeasibleAndComplementarySlack();
 45             augment();
 46         }
 47         assert isDualFeasibleAndComplementarySlack() && isPerfectMatching();    // 检查结果正确性,要求互补松弛,完美匹配
 48     }
 49
 50     private void augment()                      // 寻找最小权值路径并更新
 51     {
 52         EdgeWeightedDigraph G = new EdgeWeightedDigraph(2 * n + 2);
 53         int s = 2 * n, t = 2 * n + 1;
 54         for (int i = 0; i < n; i++)             // 所有未匹配的顶点连到 s 和 t 上,s 侧权值为 0,t 侧有权值
 55         {
 56             if (xy[i] == -1)
 57                 G.addEdge(new DirectedEdge(s, i, 0.0));
 58         }
 59         for (int j = 0; j < n; j++)
 60         {
 61             if (yx[j] == -1)
 62                 G.addEdge(new DirectedEdge(n + j, t, py[j]));
 63         }
 64         for (int i = 0; i < n; i++)
 65         {
 66             for (int j = 0; j < n; j++)         // 已匹配的顶点对添加权值为 0 的反向边,未匹配的顶点对添加修正权值的正向边
 67             {
 68                 if (xy[i] == j)
 69                     G.addEdge(new DirectedEdge(n + j, i, 0.0));
 70                 else
 71                     G.addEdge(new DirectedEdge(i, n + j, reducedCost(i, j)));
 72             }
 73         }
 74         DijkstraSP spt = new DijkstraSP(G, s);  // 计算从 s 到各顶点最短距离
 75         for (DirectedEdge e : spt.pathTo(t))    // 研究从 s 到 t 的边
 76         {
 77             int i = e.from(), j = e.to() - n;
 78             if (i < n)                          // 去掉与顶点 s 和 t 有关的部分和反向边
 79             {
 80                 xy[i] = j;
 81                 yx[j] = i;
 82             }
 83         }
 84         for (int i = 0; i < n; i++)             // 垫起各顶点的距离
 85         {
 86             px[i] += spt.distTo(i);
 87             py[i] += spt.distTo(n + i);
 88         }
 89     }
 90
 91     private double reducedCost(int i, int j)    // 顶点对之间的修正权值,用原始权值减去全局最小权值,再加上起点、终点的距离差
 92     {
 93         double reducedCost = (weight[i][j] - minWeight) + px[i] - py[j];
 94         assert reducedCost >= 0.0;
 95         if (Math.abs(reducedCost) <= FLOATING_POINT_EPSILON * (Math.abs(weight[i][j]) + Math.abs(px[i]) + Math.abs(py[j])))
 96             return 0.0;
 97         return reducedCost;
 98     }
 99
100     public double dualRow(int i)
101     {
102         return px[i];
103     }
104
105     public double dualCol(int j)
106     {
107         return py[j];
108     }
109
110     public int sol(int i)
111     {
112         return xy[i];
113     }
114
115     public double weight()                      // 输出解的权值总和
116     {
117         double total = 0.0;
118         for (int i = 0; i < n; i++)
119         {
120             if (xy[i] != -1)
121                 total += weight[i][xy[i]];
122         }
123         return total;
124     }
125
126     private boolean isDualFeasibleAndComplementarySlack()   // 检查对偶可行性和互补松弛性
127     {
128         for (int i = 0; i < n; i++)             // 检查所有边的修正权值不小于 0
129         {
130             for (int j = 0; j < n; j++)
131             {
132                 if (reducedCost(i, j) < 0)
133                 {
134                     StdOut.println("Dual variables are not feasible");
135                     return false;
136                 }
137             }
138         }
139         for (int i = 0; i < n; i++)             // 检查原变量和对偶变量的互补松弛性,即已匹配的顶点对修正权值为 0,未匹配的非 0
140         {
141             if (xy[i] != -1 && reducedCost(i, xy[i]) != 0)
142             {
143                 StdOut.println("Primal and dual variables are not complementary slack");
144                 return false;
145             }
146         }
147         return true;
148     }
149
150     private boolean isPerfectMatching()// 检查是否为完美匹配
151     {
152         boolean[] perm = new boolean[n];
153         for (int i = 0; i < n; i++)
154         {
155             if (perm[xy[i]])// ?perm[-1] 初始化为 true?
156             {
157                 StdOut.println("Not a perfect matching");
158                 return false;
159             }
160             perm[xy[i]] = true;
161         }
162         for (int j = 0; j < n; j++)// 检查 xy[] 和 yx[] 对称性
163         {
164             if (xy[yx[j]] != j)
165             {
166                 StdOut.println("xy[] and yx[] are not inverses");
167                 return false;
168             }
169         }
170         for (int i = 0; i < n; i++)
171         {
172             if (yx[xy[i]] != i)
173             {
174                 StdOut.println("xy[] and yx[] are not inverses");
175                 return false;
176             }
177         }
178         return true;
179     }
180
181     public static void main(String[] args)
182     {
183         int n = Integer.parseInt(args[0]);
184         double[][] weight = new double[n][n];
185         for (int i = 0; i < n; i++)
186         {
187             for (int j = 0; j < n; j++)
188                 weight[i][j] = StdRandom.uniform(900) + 100;  // 权值 100 ~ 999
189         }
190
191         class01 assignment = new class01(weight);
192         StdOut.printf("weight = %.0f\n\n", assignment.weight());
193
194         if (n >= 20)
195             return;
196         for (int i = 0; i < n; i++)
197         {
198             for (int j = 0; j < n; j++)
199                 StdOut.printf("%c%.0f ", (j == assignment.sol(i)) ? ‘*‘ : ‘ ‘, weight[i][j]);
200             StdOut.println();
201         }
202     }
203 }

原文地址:https://www.cnblogs.com/cuancuancuanhao/p/10066557.html

时间: 2024-10-27 10:29:16

《算法》第六章部分程序 part 7的相关文章

《算法》第一章部分程序

? 书中第一章部分程序,加上自己补充的代码.包括若干种二分搜索和寻找图上连通分量数的两种算法. ● 代码,二分搜索 1 package package01; 2 3 import java.util.Arrays; 4 import edu.princeton.cs.algs4.StdRandom; 5 6 public class class01 7 { 8 public int binarySearch(int [] a, int target) // 非递归实现 9 { 10 int lp

《深入浅出MFC》第六章 MFC程序的生死因果

SDK程序设计的第一要务是理解最重要的数个API函数的意义和用法,MFC程序设计的第一要务则是理解几个最重要的类,最基本的两个类为CWin App和CFrameWnd. 开发MFC程序需要的函数库:Windows C Runtime函数库(LIBC.LIB静态链接版本,MSVCRT.LIB,MSVCRTD.LIB).DLL Import函数库(GDI32.LIB,USER32.LIB,KERNEL32.LIB等).MFC函数库(AFX函数库)(MFC42.LIB等). STDAFX.h是作为预编

第六章应用程序的安装与管理作业

1.查看自己的主机名,并把自己的主机名修改为test.example.com(修改配置文件)2.用命令把主机名改为aa.example.com(要求永久生效)3.用配置文件修改自己的IP地址,改为172.16.30.10,子网掩码255.255.255.0,网关地址172.16.30.1,DNS地址172.16.30.254,改过之后查看是否生效4.把光盘挂载在/mnt5.使用rmp管理工具安装软件包lftp-4.4.8-8.el7.x86_64.rpm ,并且查看安装进度6.查看lftp是否安

第六章 Android应用的生命周期

=========================第六章=========================应用程序的生命周期: Android系统决定一个进程生命周期的主要因素包括: 1.系统当前剩余多少可用内存--内存不足时,杀死一些进程释放内存 2.该进程对于用户的重要性--优先杀死对用户不重要的进程 1.前台进程---运行在前台,可与用户直接交互的进程 2.可见进程---用户可见,暂时被遮挡,暂时不能与用户交互的进程 3.服务进程---在后台启动了Service的进程 4.后台进程---为

读《程序是怎么跑起来的》第六章有感

第六章亲自尝试压缩数据,知道了文件是字节数据的集合体,LZH是用LHA等工具压缩过的扩展名,例如AAABB这个数据压缩后就是A3B2,半角英文数字是用1个字节来表示的,汉字等全角字符是用两个字节来表示的,知道了压缩后数据能复原的就是可逆压缩,不能复原的就是非可逆压缩. 文件是以字节(B=Byte)为单位来储存的,文件就是字节数据的集合,RLE的算法机制,简称为把文件内容用"数据×重复次数的形式"来表示的压缩方法.但是RLE的算法也有缺点,在实际的文本文件中,同样字符多次出现的文件并不多

算法入门经典第六章 例题6-14 Abbott的复仇(Abbott&#39;s Revenge)BFS算法实现

Sample Input 3 1 N 3 3 1 1 WL NR * 1 2 WLF NR ER * 1 3 NL ER * 2 1 SL WR NF * 2 2 SL WF ELF * 2 3 SFR EL * 0 Sample Output (3,1) (2,1) (1,1) (1,2) (2,2) (2,3) (1,3) (1,2) (1,1) (2,1) (2,2) (1,2) (1,3) (2,3) (3,3) 析 题目的大意是,输入起点,离开起点时的朝向和终点,求一条最短路. 每一个

第六章 常见排序算法

上章回顾 二叉树的定义 树深度的定义 什么样的二叉树是满二叉树 中序遍历的规则 [email protected]:Kevin-Dfg/[email protected]:Kevin-Dfg/Data-Structures-and-Algorithm-Analysis-in-C.git 第六章 第六章 常见排序算法 常见排序算法 [email protected]:Kevin-Dfg/[email protected]:Kevin-Dfg/Data-Structures-and-Algorith

2014年软考程序员-常考知识点复习笔记【第六章】

51CTO学院,在软考备考季特别整理了"2014年软考程序员-常考知识点复习笔记[汇总篇]",帮助各位学院顺利过关!更多软件水平考试辅导及试题,请关注51CTO学院-软考分类吧! 查看汇总:2014年软考程序员-常考知识点复习笔记[汇总篇]  OSI模型7层结构,TCP/IP模型结构? osi参考模型 osi参考模型中的数据封装过程 下面的图表试图显示不同的TCP/IP和其他的协议在最初OSI模型中的位置: 7应用层例如HTTP.SMTP.SNMP.FTP.Telnet.SIP.SSH

【WPF学习】第二十六章 Application类——应用程序的生命周期

原文:[WPF学习]第二十六章 Application类--应用程序的生命周期 在WPF中,应用程序会经历简单的生命周期.在应用程序启动后,将立即创建应用程序对象,在应用程序运行时触发各种应用程序事件,你可以选择监视其中的某些事件.最后,当释放应用程序对象时,应用程序将结束. 一.创建Application对象 使用Application类的最简单方式是手动创建它.下面的示例演示了最小的程序:在应用程序入口(Main()方法)处创建名为MainWindow的窗口,并启动一个新的应用程序: 在本质