走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;
}