走AB这条路的前提是: B到home的最短路比A到home的最短路要短,求满足这个要求的office到home的路有多少条
SPFA:参考http://sgeteternal.iteye.com/blog/1148891
spfa比dijkstra极大程度减少不必要的松弛操作。故而比dij快。
#include <iostream> #include <vector> #include <cstdio> #include <cstdlib> #include <queue> #include <memory.h> using namespace std; #define MAXI 1011 #define INFI 0x7fffffff #define LLD long long struct V { int v, w; //v, w[u, v] }; vector<V> g[MAXI]; //邻接表 int n, dis[MAXI]; //|V|, d[] bool inQ[MAXI]; //入队标记 void SPFA(int u) //源点 { int i, v, w; queue<int> Q; //队列 for (i = 0; i < n; i++) //初始化 { dis[i] = INFI; inQ[i] = false; } Q.push(u); //压入源点 inQ[u] = true; dis[u] = 0; while (!Q.empty()) //队列不为空 { u = Q.front(); Q.pop(); inQ[u] = false; for (i = 0; i < g[u].size(); i++) { v = g[u][i].v; w = g[u][i].w; if (dis[v] - w > dis[u]) //符合三角不等式2 { dis[v] = w + dis[u]; //松弛操作 if (inQ[v] == false) //判断v系唔系队列 { Q.push(v); //唔系就入队列 inQ[v] = true; } } } } }
所以SPFA其实就是大家熟悉的 bfs + 松弛操作
松弛操作:应该就是判断更新。
负回路版本只需要加上一个数组记录每个点入队次数就得。
记忆化搜索:算法上依然是搜索的流程,但是搜索到的一些解用动态规划的那种思想和模式作一些保存。
一般说来,动态规划总要遍历所有的状态,而搜索可以排除一些无效状态。
更重要的是搜索还可以剪枝,可能剪去大量不必要的状态,因此在空间开销上往往比动态规划要低很多。
记忆化算法在求解的时候还是按着自顶向下的顺序,但是每求解一个状态,就将它的解保存下来,
以后再次遇到这个状态的时候,就不必重新求解了。
这种方法综合了搜索和动态规划两方面的优点,因而还是很有实用价值的。
可以归纳为:记忆化搜索=搜索的形式+动态规划的思想
dfs记忆搜索
#include <iostream> #include <fstream> #include <algorithm> #include <string> #include <set> #include <map> #include <queue> #include <utility> #include <stack> #include <list> #include <vector> #include <cstdio> #include <cstdlib> #include <cstring> #include <cmath> //#include <ctime> #include <ctype.h> using namespace std; #define L long long #define inf 0x3fffffff #define M 1005 struct road{ int v, w; }; int n, dist[M], dp[M]; vector<road> g[M]; //保存每个顶点有多少条路径与其它顶点相连 bool inq[M]; void spfa (int u) //spfa求home到各个点的最短路 { int i, v, w; for (i = 1; i <= n; i++) { dist[i] = inf; inq[i] = false; } queue<int> q; q.push (u); dist[u] = 0; inq[u] = true; while (!q.empty()) { u = q.front(); q.pop(); inq[u] = false; for (i = 0; i < g[u].size(); i++) { v = g[u][i].v; w = g[u][i].w; if (dist[u] + w < dist[v]) //判断是否可以构造出到v的最短路 { dist[v] = dist[u] + w; //所谓的松弛 if (!inq[v]) { q.push (v); inq[v] = true; } } } } } int dfs (int u) //记忆化搜索 { if (u == 2) return 1; if (dp[u] > 0) //核心 return dp[u]; for (int i = 0; i < g[u].size(); i++) { int v = g[u][i].v; if (dist[v] < dist[u]) //满足题意【v相当于B,u相当于A】 dp[u] += dfs (v); } return dp[u]; } int main() { int m, u, v, w, i; road x; while (scanf ("%d", &n), n) { for (i = 1; i <= n; i++) g[i].clear(); scanf ("%d", &m); while (m--) { scanf ("%d%d%d", &u, &v, &w); x.v = v, x.w = w; g[u].push_back (x); x.v = u; g[v].push_back (x); } spfa (2); //逆向思维,把终点当成起点 memset (dp, 0, sizeof(dp)); printf ("%d\n", dfs (1)); } return 0; }