Johnson 全源最短路徑算法

解決單源最短路徑問題(Single Source Shortest Paths Problem)的算法包括:

  • Dijkstra 單源最短路徑算法:時間復雜度為 O(E + VlogV),要求權值非負;
  • Bellman-Ford 單源最短路徑算法:時間復雜度為 O(VE),適用于帶負權值情況;

對于全源最短路徑問題(All-Pairs Shortest Paths Problem),可以認為是單源最短路徑問題的推廣,即分別以每個頂點作為源頂點并求其至其它頂點的最短距離。例如,對每個頂點應用?Bellman-Ford 算法,則可得到所有頂點間的最短路徑的運行時間為?O(V2E),由于圖中頂點都是連通的,而邊的數量可能會比頂點更多,這個時間沒有比?Floyd-Warshall?全源最短路徑算法?O(V3) 更優。那么,再試下對每個頂點應用?Dijkstra 算法,則可得到所有頂點間的最短路徑的運行時間為?O(VE + V2logV),看起來優于 Floyd-Warshall 算法的 O(V3),所以看起來使用基于?Dijkstra 算法的改進方案好像更好,但問題是?Dijkstra 算法要求圖中所有邊的權值非負,不適合通用的情況。

在 1977 年,Donald B. Johnson?提出了對所有邊的權值進行 "re-weight" 的算法,使得邊的權值非負,進而可以使用?Dijkstra 算法進行最短路徑的計算。

我們先自己思考下如何進行?"re-weight" 操作,比如,簡單地對每條邊的權值加上一個較大的正數,使其非負,是否可行?

   1     1     1
s-----a-----b-----c\              /\          /\______/4

比如上面的圖中,共 4 條邊,權值分別為 1,1,1,4。當前 s --> c 的最短路徑是 {s-a, a-b, b-c} 即 1+1+1=3。而如果將所有邊的權值加 1,則最短路徑就會變成 {s-c} 的 5,因為 2+2+2=6,實際上導致了最短路徑的變化,顯然是錯誤的。

那么,Johnson 算法是如何對邊的權值進行?"re-weight" 的呢?以下面的圖 G 為例,有 4 個頂點和 5 條邊。

首先,新增一個源頂點 4,并使其與所有頂點連通,新邊賦權值為 0,如下圖所示。

使用?Bellman-Ford 算法?計算新的頂點到所有其它頂點的最短路徑,則從 4 至 {0, 1, 2, 3} 的最短路徑分別是 {0, -5, -1, 0}。即有 h[] =?{0, -5, -1, 0}。當得到這個 h[] 信息后,將新增的頂點 4 移除,然后使用如下公式對所有邊的權值進行 "re-weight":

w(u, v) = w(u, v) + (h[u] - h[v]).

則可得到下圖中的結果:

此時,所有邊的權值已經被 "re-weight" 為非負。此時,就可以利用?Dijkstra 算法對每個頂點分別進行最短路徑的計算了。

Johnson 算法描述如下:

  1. 給定圖 G = (V, E),增加一個新的頂點 s,使 s 指向圖 G 中的所有頂點都建立連接,設新的圖為 G’;
  2. 對圖 G’ 中頂點 s?使用?Bellman-Ford 算法計算單源最短路徑,得到結果 h[] = {h[0], h[1], .. h[V-1]};
  3. 對原圖 G 中的所有邊進行 "re-weight",即對于每個邊 (u, v),其新的權值為?w(u, v) + (h[u] - h[v]);
  4. 移除新增的頂點 s,對每個頂點運行?Dijkstra 算法求得最短路徑;

Johnson 算法的運行時間為?O(V2logV + VE)。

Johnson 算法偽碼實現如下:

Johnson 算法?C# 代碼實現如下:

復制代碼
  1 using System;
  2 using System.Collections.Generic;
  3 using System.Linq;
  4 
  5 namespace GraphAlgorithmTesting
  6 {
  7   class Program
  8   {
  9     static void Main(string[] args)
 10     {
 11       // build a directed and negative weighted graph
 12       Graph directedGraph1 = new Graph(5);
 13       directedGraph1.AddEdge(0, 1, -1);
 14       directedGraph1.AddEdge(0, 2, 4);
 15       directedGraph1.AddEdge(1, 2, 3);
 16       directedGraph1.AddEdge(1, 3, 2);
 17       directedGraph1.AddEdge(1, 4, 2);
 18       directedGraph1.AddEdge(3, 2, 5);
 19       directedGraph1.AddEdge(3, 1, 1);
 20       directedGraph1.AddEdge(4, 3, -3);
 21 
 22       Console.WriteLine();
 23       Console.WriteLine("Graph Vertex Count : {0}", directedGraph1.VertexCount);
 24       Console.WriteLine("Graph Edge Count : {0}", directedGraph1.EdgeCount);
 25       Console.WriteLine();
 26 
 27       int[,] distSet1 = directedGraph1.Johnsons();
 28       PrintSolution(directedGraph1, distSet1);
 29 
 30       // build a directed and positive weighted graph
 31       Graph directedGraph2 = new Graph(4);
 32       directedGraph2.AddEdge(0, 1, 5);
 33       directedGraph2.AddEdge(0, 3, 10);
 34       directedGraph2.AddEdge(1, 2, 3);
 35       directedGraph2.AddEdge(2, 3, 1);
 36 
 37       Console.WriteLine();
 38       Console.WriteLine("Graph Vertex Count : {0}", directedGraph2.VertexCount);
 39       Console.WriteLine("Graph Edge Count : {0}", directedGraph2.EdgeCount);
 40       Console.WriteLine();
 41 
 42       int[,] distSet2 = directedGraph2.Johnsons();
 43       PrintSolution(directedGraph2, distSet2);
 44 
 45       Console.ReadKey();
 46     }
 47 
 48     private static void PrintSolution(Graph g, int[,] distSet)
 49     {
 50       Console.Write("\t");
 51       for (int i = 0; i < g.VertexCount; i++)
 52       {
 53         Console.Write(i + "\t");
 54       }
 55       Console.WriteLine();
 56       Console.Write("\t");
 57       for (int i = 0; i < g.VertexCount; i++)
 58       {
 59         Console.Write("-" + "\t");
 60       }
 61       Console.WriteLine();
 62       for (int i = 0; i < g.VertexCount; i++)
 63       {
 64         Console.Write(i + "|\t");
 65         for (int j = 0; j < g.VertexCount; j++)
 66         {
 67           if (distSet[i, j] == int.MaxValue)
 68           {
 69             Console.Write("INF" + "\t");
 70           }
 71           else
 72           {
 73             Console.Write(distSet[i, j] + "\t");
 74           }
 75         }
 76         Console.WriteLine();
 77       }
 78     }
 79 
 80     class Edge
 81     {
 82       public Edge(int begin, int end, int weight)
 83       {
 84         this.Begin = begin;
 85         this.End = end;
 86         this.Weight = weight;
 87       }
 88 
 89       public int Begin { get; private set; }
 90       public int End { get; private set; }
 91       public int Weight { get; private set; }
 92 
 93       public void Reweight(int newWeight)
 94       {
 95         this.Weight = newWeight;
 96       }
 97 
 98       public override string ToString()
 99       {
100         return string.Format(
101           "Begin[{0}], End[{1}], Weight[{2}]",
102           Begin, End, Weight);
103       }
104     }
105 
106     class Graph
107     {
108       private Dictionary<int, List<Edge>> _adjacentEdges
109         = new Dictionary<int, List<Edge>>();
110 
111       public Graph(int vertexCount)
112       {
113         this.VertexCount = vertexCount;
114       }
115 
116       public int VertexCount { get; private set; }
117 
118       public int EdgeCount
119       {
120         get
121         {
122           return _adjacentEdges.Values.SelectMany(e => e).Count();
123         }
124       }
125 
126       public void AddEdge(int begin, int end, int weight)
127       {
128         if (!_adjacentEdges.ContainsKey(begin))
129         {
130           var edges = new List<Edge>();
131           _adjacentEdges.Add(begin, edges);
132         }
133 
134         _adjacentEdges[begin].Add(new Edge(begin, end, weight));
135       }
136 
137       public void AddEdge(Edge edge)
138       {
139         AddEdge(edge.Begin, edge.End, edge.Weight);
140       }
141 
142       public void AddEdges(IEnumerable<Edge> edges)
143       {
144         foreach (var edge in edges)
145         {
146           AddEdge(edge);
147         }
148       }
149 
150       public IEnumerable<Edge> GetAllEdges()
151       {
152         return _adjacentEdges.Values.SelectMany(e => e);
153       }
154 
155       public int[,] Johnsons()
156       {
157         // distSet[,] will be the output matrix that will finally have the shortest 
158         // distances between every pair of vertices
159         int[,] distSet = new int[VertexCount, VertexCount];
160 
161         for (int i = 0; i < VertexCount; i++)
162         {
163           for (int j = 0; j < VertexCount; j++)
164           {
165             distSet[i, j] = int.MaxValue;
166           }
167         }
168         for (int i = 0; i < VertexCount; i++)
169         {
170           distSet[i, i] = 0;
171         }
172 
173         // step 1: add new vertex s and connect to all vertices
174         Graph g = new Graph(this.VertexCount + 1);
175         g.AddEdges(this.GetAllEdges());
176 
177         int s = this.VertexCount;
178         for (int i = 0; i < this.VertexCount; i++)
179         {
180           g.AddEdge(s, i, 0);
181         }
182 
183         // step 2: use Bellman-Ford to evaluate shortest paths from s
184         int[] h = g.BellmanFord(s);
185 
186         // step 3: re-weighting edges of the original graph
187         //         w(u, v) = w(u, v) + (h[u] - h[v])
188         foreach (var edge in this.GetAllEdges())
189         {
190           edge.Reweight(edge.Weight + (h[edge.Begin] - h[edge.End]));
191         }
192 
193         // step 4: use Dijkstra for each edges
194         for (int begin = 0; begin < this.VertexCount; begin++)
195         {
196           int[] dist = this.Dijkstra(begin);
197           for (int end = 0; end < dist.Length; end++)
198           {
199             if (dist[end] != int.MaxValue)
200             {
201               distSet[begin, end] = dist[end] - (h[begin] - h[end]);
202             }
203           }
204         }
205 
206         return distSet;
207       }
208 
209       public int[,] FloydWarshell()
210       {
211         /* distSet[,] will be the output matrix that will finally have the shortest 
212            distances between every pair of vertices */
213         int[,] distSet = new int[VertexCount, VertexCount];
214 
215         for (int i = 0; i < VertexCount; i++)
216         {
217           for (int j = 0; j < VertexCount; j++)
218           {
219             distSet[i, j] = int.MaxValue;
220           }
221         }
222         for (int i = 0; i < VertexCount; i++)
223         {
224           distSet[i, i] = 0;
225         }
226 
227         /* Initialize the solution matrix same as input graph matrix. Or 
228            we can say the initial values of shortest distances are based
229            on shortest paths considering no intermediate vertex. */
230         foreach (var edge in _adjacentEdges.Values.SelectMany(e => e))
231         {
232           distSet[edge.Begin, edge.End] = edge.Weight;
233         }
234 
235         /* Add all vertices one by one to the set of intermediate vertices.
236           ---> Before start of a iteration, we have shortest distances between all
237           pairs of vertices such that the shortest distances consider only the
238           vertices in set {0, 1, 2, .. k-1} as intermediate vertices.
239           ---> After the end of a iteration, vertex no. k is added to the set of
240           intermediate vertices and the set becomes {0, 1, 2, .. k} */
241         for (int k = 0; k < VertexCount; k++)
242         {
243           // Pick all vertices as source one by one
244           for (int i = 0; i < VertexCount; i++)
245           {
246             // Pick all vertices as destination for the above picked source
247             for (int j = 0; j < VertexCount; j++)
248             {
249               // If vertex k is on the shortest path from
250               // i to j, then update the value of distSet[i,j]
251               if (distSet[i, k] != int.MaxValue
252                 && distSet[k, j] != int.MaxValue
253                 && distSet[i, k] + distSet[k, j] < distSet[i, j])
254               {
255                 distSet[i, j] = distSet[i, k] + distSet[k, j];
256               }
257             }
258           }
259         }
260 
261         return distSet;
262       }
263 
264       public int[] BellmanFord(int source)
265       {
266         // distSet[i] will hold the shortest distance from source to i
267         int[] distSet = new int[VertexCount];
268 
269         // Step 1: Initialize distances from source to all other vertices as INFINITE
270         for (int i = 0; i < VertexCount; i++)
271         {
272           distSet[i] = int.MaxValue;
273         }
274         distSet[source] = 0;
275 
276         // Step 2: Relax all edges |V| - 1 times. A simple shortest path from source
277         // to any other vertex can have at-most |V| - 1 edges
278         for (int i = 1; i <= VertexCount - 1; i++)
279         {
280           foreach (var edge in _adjacentEdges.Values.SelectMany(e => e))
281           {
282             int u = edge.Begin;
283             int v = edge.End;
284             int weight = edge.Weight;
285 
286             if (distSet[u] != int.MaxValue
287               && distSet[u] + weight < distSet[v])
288             {
289               distSet[v] = distSet[u] + weight;
290             }
291           }
292         }
293 
294         // Step 3: check for negative-weight cycles.  The above step guarantees
295         // shortest distances if graph doesn't contain negative weight cycle.
296         // If we get a shorter path, then there is a cycle.
297         foreach (var edge in _adjacentEdges.Values.SelectMany(e => e))
298         {
299           int u = edge.Begin;
300           int v = edge.End;
301           int weight = edge.Weight;
302 
303           if (distSet[u] != int.MaxValue
304             && distSet[u] + weight < distSet[v])
305           {
306             Console.WriteLine("Graph contains negative weight cycle.");
307           }
308         }
309 
310         return distSet;
311       }
312 
313       public int[] Dijkstra(int source)
314       {
315         // dist[i] will hold the shortest distance from source to i
316         int[] distSet = new int[VertexCount];
317 
318         // sptSet[i] will true if vertex i is included in shortest
319         // path tree or shortest distance from source to i is finalized
320         bool[] sptSet = new bool[VertexCount];
321 
322         // initialize all distances as INFINITE and stpSet[] as false
323         for (int i = 0; i < VertexCount; i++)
324         {
325           distSet[i] = int.MaxValue;
326           sptSet[i] = false;
327         }
328 
329         // distance of source vertex from itself is always 0
330         distSet[source] = 0;
331 
332         // find shortest path for all vertices
333         for (int i = 0; i < VertexCount - 1; i++)
334         {
335           // pick the minimum distance vertex from the set of vertices not
336           // yet processed. u is always equal to source in first iteration.
337           int u = CalculateMinDistance(distSet, sptSet);
338 
339           // mark the picked vertex as processed
340           sptSet[u] = true;
341 
342           // update dist value of the adjacent vertices of the picked vertex.
343           for (int v = 0; v < VertexCount; v++)
344           {
345             // update dist[v] only if is not in sptSet, there is an edge from 
346             // u to v, and total weight of path from source to  v through u is 
347             // smaller than current value of dist[v]
348             if (!sptSet[v]
349               && distSet[u] != int.MaxValue
350               && _adjacentEdges.ContainsKey(u)
351               && _adjacentEdges[u].Exists(e => e.End == v))
352             {
353               int d = _adjacentEdges[u].Single(e => e.End == v).Weight;
354               if (distSet[u] + d < distSet[v])
355               {
356                 distSet[v] = distSet[u] + d;
357               }
358             }
359           }
360         }
361 
362         return distSet;
363       }
364 
365       private int CalculateMinDistance(int[] distSet, bool[] sptSet)
366       {
367         int minDistance = int.MaxValue;
368         int minDistanceIndex = -1;
369 
370         for (int v = 0; v < VertexCount; v++)
371         {
372           if (!sptSet[v] && distSet[v] <= minDistance)
373           {
374             minDistance = distSet[v];
375             minDistanceIndex = v;
376           }
377         }
378 
379         return minDistanceIndex;
380       }
381     }
382   }
383 }
復制代碼

運行結果如下:

參考資料

  • 廣度優先搜索
  • 深度優先搜索
  • Breadth First Traversal for a Graph
  • Depth First Traversal for a Graph
  • Dijkstra 單源最短路徑算法
  • Bellman-Ford 單源最短路徑算法
  • Bellman–Ford algorithm
  • Introduction To Algorithm
  • Floyd-Warshall's algorithm
  • Bellman-Ford algorithm for single-source shortest paths
  • Dynamic Programming | Set 23 (Bellman–Ford Algorithm)
  • Dynamic Programming | Set 16 (Floyd Warshall Algorithm)
  • Johnson’s algorithm for All-pairs shortest paths
  • Floyd-Warshall's algorithm
  • 最短路徑算法--Dijkstra算法,Bellmanford算法,Floyd算法,Johnson算法
  • QuickGraph, Graph Data Structures And Algorithms for .NET
  • CHAPTER 26: ALL-PAIRS SHORTEST PATHS





本文轉自匠心十年博客園博客,原文鏈接:http://www.cnblogs.com/gaochundong/p/johnson_algorithm.html,如需轉載請自行聯系原作者


本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/453421.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/453421.shtml
英文地址,請注明出處:http://en.pswp.cn/news/453421.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

單循環鏈表中設置尾指針比設置頭指針更好的原因

尾指針是指向終端結點的指針&#xff0c;用它來表示單循環鏈表可以使得查找鏈表的開始結點和終端結點都很方便。 設一帶頭結點的單循環鏈表&#xff0c;其尾指針為rear&#xff0c;則開始結點和終端結點的位置分別是rear->next->next和rear,查找時間都是O(1)。 若用頭指…

為何大部分人成不了技術專家?

此文為我在CSDN的新的SNS里看到的&#xff0c;感觸很深&#xff0c;和大家分享一下.里面的許多人的觀點都讓我受益匪淺。 如果你是項目經理&#xff0c;產品經理或者架構師&#xff0c;我真誠邀請你加入 如果你還是學生或者還是初學者&#xff0c;我建議你先等等&#xff0c;…

Machine Learning 學習筆記1 - 基本概念以及各分類

What is machine learning? 并沒有廣泛認可的定義來準確定義機器學習。以下定義均為譯文&#xff0c;若以后有時間&#xff0c;將補充原英文...... 定義1、來自Arthur Samuel&#xff08;上世紀50年代、西洋棋程序&#xff09; 在進行特定編程的情況下給予計算機學習能力的領域…

值傳遞與地址傳遞

值傳遞與地址傳遞的區別&#xff1a;兩者其實傳遞的都是一個內存單元的內容。不同的是&#xff0c;值傳遞傳遞的內容是一個變量的值&#xff0c;得到這個值后&#xff0c;對這個值的修改不能改變原變量的值&#xff1b;而地址傳遞傳遞的是一個變量的地址&#xff0c;得到傳遞的…

蒙特 卡羅方法matlab,蒙特·卡羅方法中的數學之美,你一定不想錯過

原標題&#xff1a;蒙特卡羅方法中的數學之美&#xff0c;你一定不想錯過有方教育——我們致力于為中學生提供學界和業界前沿的學術科研教育內容&#xff0c;幫助學生參加海外科研項目&#xff0c;在提升申請競爭力的同時&#xff0c;獲得領跑優勢。一、概述蒙特卡羅方法(Monte…

【 CDN 最佳實踐】CDN 命中率優化思路

CDN 在靜態資源的加速場景中是將靜態資源緩存在距離客戶端較近的CDN 節點上&#xff0c;然后客戶端訪問該資源即可通過較短的鏈路直接從緩存中獲取資源&#xff0c;而避免再通過較長的鏈路回源獲取靜態資源。因此 CDN的緩存命中率的高低直接影響客戶體驗&#xff0c;而保證較高…

職場新人的入門法則:少想、多做、立即執行!

對于剛進入職場的新人來說&#xff0c;要想在工作中快速獲得成長&#xff0c;唯一辦法就是&#xff1a;“少想&#xff0c;多做&#xff0c;立即執行&#xff01;”。 少想不等于盲目&#xff0c;在保證工作思路絕對清晰的同時&#xff0c;執行力越高&#xff0c;執行速度越快…

Python基礎-time and datetime

一、在Python中&#xff0c;通常有這幾種方式來表示時間&#xff1a; 時間戳格式化的時間字符串元組&#xff08;struct_time&#xff09;共九個元素。由于Python的time模塊實現主要調用C庫&#xff0c;所以各個平臺可能有所不同。1.時間戳&#xff08;timestamp&#xff09;的…

實際應用中帶頭節點的線性鏈表

/*帶頭節點的線性鏈表類型*/ typedef char ElemType//結點類型 typedef struct LNode {char data;struct LNode *next; }*Link,*Position;//鏈表類型 typedef struct {Link head,tail;int len; }LinkList;/**/ /*一些在其他函數定義中會調用的函數*/ /**//*---compare---比較兩…

matlab中歐姆如何表示,在excel中歐姆符號怎么打

在excel中歐姆符號怎么打&#xff0c;相信對于好多熟練用excel的朋友來說&#xff0c;是很簡單不過的&#xff0c;但是對于有些初學者來說&#xff0c;就是菜鳥啦&#xff0c;就有點懵懵懂懂的感覺了&#xff0c;畢竟剛接觸的東西還沒用過嘛。但是&#xff0c;沒關系今天筆者就…

原生js系列之DOM工廠模式

寫在前面 如今&#xff0c;在項目中使用React、Vue等框架作為技術棧已成為一種常態&#xff0c;在享受帶來便利性的同時&#xff0c;也許我們漸漸地遺忘原生js的寫法。 現在&#xff0c;是時候回歸本源&#xff0c;響應原始的召喚了。本文將一步一步帶領大家封裝一套屬于自己的…

武術與軟件設計 - 簡單即是最好

偶然間在公車上看見一個講中國功夫的特輯&#xff0c;說道香港武打片的發展歷程&#xff0c;當然就不得不提起李小龍先生&#xff0c;我們知道他截拳道的威力&#xff0c;這時候我記得在看李小龍傳奇時他所說的一些話&#xff0c;當他和美國一個高手比武后他輸了&#xff0c;最…

matlab的概述,Matlab概述

MATLAB(矩陣實驗室)是數字計算&#xff0c;可視化和編程的第四代高級編程語言和交互式環境。MATLAB是由MathWorks開發的。它允許矩陣操縱&#xff0c;繪制功能和數據; 實現算法; 創建用戶界面; 與其他語言編寫的程序(包括C語言&#xff0c;C&#xff0c;Java和FORTRAN)進行交互…

形參和實參

形參&#xff1a;全稱為“形式參數”是在定義函數名和函數體的時候使用的參數&#xff0c;目的是用來接收調用該函數時傳遞的參數。形參的作用是實現主調函數與被調函數之間的聯系&#xff0c;通常將函數所處理的數據&#xff0c;影響函數功能的因素或者函數處理的結果作為形參…

sizeof和strlen的區別

strlen——get the length of a string.size_t strlen(const char *string);Each ofthese functions returns the number of characters instring, notincluding the terminating null character.//函數返回string里的字符數&#xff0c;不包括終止字符\0sizeofThe sizeof keyw…

位置參數及操作符號

特殊字符對應的處理參數&#xff1a; 參數說明$0當前執行的腳本文件名&#xff0c;若全路徑執行&#xff0c;則顯示腳本路徑$n當前執行腳本的第n個參數值&#xff0c;若n>9&#xff0c;則需寫成${10}$#當前傳參總個數$$腳本運行的當前進程ID號,用例&#xff1a;當一個進程重…

python變量命名可以有特殊符號嗎,和孩子一起學習python之變量命名規則

下面是關于變量名(也稱為標識符)的一些規則必須以一個字母或一個下劃線字符開頭。后面可以使用一個字母、數字或下劃線字符的序列&#xff0c;長度不限。字母可以是大寫或小寫&#xff0c;大小寫是不同的。也就是說&#xff0c;Ax不同于aX。數字可以是從0到9(包括0到9)的任意數…

C語言中*和

(一) 在定義時&#xff0c;* 是一個標識符&#xff0c;聲明該變量是一個指針&#xff0c;比如說int *p; 那p就是一個指向int型的指針&#xff1b; 在調用時&#xff0c; &#xff08;1&#xff09;*p是指指針p指向的那個變量&#xff0c;比如說之前有int a5&#xff1b;int …

IT人的好習慣和不良習慣總結

好習慣&#xff1a; 細節一&#xff1a;在電腦旁放上幾盆植物&#xff0c;傳說仙人掌可以有效地吸收輻射&#xff0c;但是會扎到人&#xff0c;而且有沒效果也沒科學根據&#xff0c;不推薦&#xff1b;其實只要是綠色植物就可以&#xff0c;植物可以讓你多點氧氣&#xff0c;保…

【BZOJ 3326】[Scoi2013]數數 數位dp+矩陣乘法優化

挺好的數位dp……先說一下我個人的做法:經過觀察,發現這題按照以往的思路從后往前遞增,不怎么好推,然后我就大膽猜想,從前往后推,發現很好推啊,維護四個變量,從開始位置到現在有了i個數 f[i]:所有數的所有未包含最后一位的子串的和 s[i]:所有數的所有后綴子串的和 c[i]:所有數的…