一、单源最短路问题1 Dijstra:无负权图
1.邻接矩阵:O(|V|*|V|)
int G[maxn][maxn];
int d[maxn];
bool vis[maxn];
int n;
void Dijkstra(int s)
{
for(int i=1;i<=n;i++){
d[i] = inf;
vis[i] = false;
}
d[s] = 0;
while(1){
int u = -1;
for(int v=1;v<=n;v++){
if(!vis[v]&&(u==-1||d[u]>d[v])){
u = v;
}
}
if(u==-1) break;
vis[u] = true;
for(int v=1;v<=n;v++){
d[v] = min(d[v],d[u]+G[u][v]);
}
}
}
2.邻接表:O(|E|log|V|)
struct edge{
int to,cost;
};
typedef pair<int,int> P; //fisrt是最短距离,second是顶点编号
int n;
vector<edge> Adj[maxn];
void Init()
{
for(int i=1;i<=n;i++)
Adj[i].clear();
}
void add(int u,int v,int c)
{
edge tmp;
tmp.to = v;
tmp.cost = c;
Adj[u].push_back(tmp);
}
void Dijsktra(int s)
{
//通过制定greater<P>参数,堆按照first从小到大的顺序取出值
priority_queue<P,vector<P>,greater<P>> q;
for(int i=1;i<=n;i++)
d[i] = inf;
d[s] = 0;
q.push(P(0,s));
while(!q.empty()){
P p = q.top();
q.pop();
int v = p.second;
if(d[v] < p.first) continue;
for(int i=0;i<Adj[v].size();i++){
edge e = Adj[v][i];
if(d[e.to]>d[v] + e.cost){
d[e.to] = d[v] + e.cost;
q.push(P(d[e.to],e.to));
}
}
}
}
3.Dijkstra + DFS
输出最短路径
int G[maxn][maxn];
int d[maxn];
bool vis[maxn];
int optvalue; //第二标尺最优值
vector<int> pre[maxn]; //存放结点的前驱结点
vector<int> path,tempPath; //最优路径、临时路径
void Dijstra(int s)
{
fill(d,d+maxn,inf);
d[s] = 0;
while(1){
int u = -1;
for(int v=1;v<=n;v++){
if(!vis(v)&&(u==-1||d[u]>d[v])){
u = v;
}
}
if(u==-1) break;
vis[u] = true;
for(int v=1;v<=n;v++){
if(!vis[v]&&G[u][v]!=inf){
if(d[u]+G[u][v]<d[v]){
d[v] = d[u]+G[u][v];
pre[v].clear();
pre[v].push_back(u);
}
else if(d[u]+G[u][v]==d[v]){
pre[v].push_back(u);
}
}
}
}
}
void DFS(int v) //当前访问结点
{
if(v == st){ //如果到达了叶子结点st(即路径起点)
tempPath.push_back(v); //将起点st加入临时路径tempPath的后面
int value;
计算路径tempPath上的value值
if(value优于optvalue){
optvalue = value;
path = tempPath;
}
tempPath.pop_back(); //将刚加入的结点删除
return ;
}
//递归式
tempPath.push_back(v);
for(int i=0;i<pre[v].size();i++)
DFS(pre[v][i]);
temPath.pop_back();
}
计算路径tempPath上的value值
if(value优于optvalue){
optvalue = value;
path = tempPath;
}
//边权之和
int value = 0;
for(int i=tempPath.size()-1;i>=0;i--){
int id = tempPath[i];
int idNext = tempPath[i-1];
value += G[id][idNext];
}
//点权之和
int value = 0;
for(int i=tempPath.size()-1;i>=0;i--){
int id = tempPath[i];
value += w[id];
}
3.SPFA 期望时间复杂度O(kE),最差O(VE)
vector<node> Adj[maxn]; //图G的邻接表
int n,num[maxn];
bool vis[maxn];
bool SPFA(int s)
{
for(int i=1;i<=n;i++){
num[i] = 0;
d[i] = inf;
vis[i] = false;
}
queue<int> q;
q.push(s);
num[s]++;
vis[s] = true;
while(!q.empty()){
int u = q.front();
q.pop();
vis[u] = false;
for(int j=0;j<Adj[u].size();j++){
int vv = Adj[u][j].v;
int dd = Adj[u][j].d;
if(d[u]+dd<d[vv]){
d[vv] = d[u] + dd;
if(!vis[vv]){
q.push(vv);
num[vv]++;
vis[vv] = true;
if(num[vv]>=n) return false;
}
}
}
}
return true;
}
3.任意两点间的最短路问题
Floyd-Warshall O(|V|3)
int d[maxn][maxn];
int n;
void warshall_floyed(){
for(int k=1;k<=n;k++){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
d[i][j] = min(d[i][j],d[i][k]+d[k][j]);
}
}
}
}
4.路径还原
以Dijkstra为例
int pre[maxn],d[maxn];
bool vis[maxn];
int n;
void Dijkstra(int s)
{
for(int i=1;i<=n;i++){
pre[i] = -1;
d[i] = inf;
vis[i] = false;
}
d[s] = 0;
while(1){
int u = -1;
for(int v=1;v<=n;v++){
if(!vis[v]&&(u==-1||d[u]>d[v])){
u = v;
}
}
if(u==-1) break;
vis[u] = true;
for(int v=1;v<=n;v++){
if(d[v]>d[u]+ Adj[u][v]){
d[v] = d[u] + Adj[u][v];
pre[v] = u;
}
}
}
}
vector<int> get_path(int t)
{
vector<int> path;
for( ;t!=-1;t=pre[t]) path.push_back(t); //得到的是t到s的顺序,翻转即可
reverse(path.begin(),path.end());
return path;
}
1、最小生成树问题1(Prim算法)
O(|V|^2) 堆维护O(|E|lon|V|)
int G[maxn][maxn];
int d[maxn];
bool vis[maxn];
int n;
int prim(int s)
{
for(int i=1;i<=n;i++){
d[i] = inf;
vis[i] = false;
}
d[s] = 0;
int res = 0;
while(1){
int u = -1;
for(int v=1;v<=n;v++){
if(!vis[v]&&(u==-1||d[u]>d[v])){
u = v;
}
}
if(u==-1) break;
vis[u] = true;
res += d[u];
for(int v=1;v<=n;v++){
if(!vis[v]&&d[v]>G[u][v]){
d[v] = G[u][v];
}
}
}
return res;
}
2.最小生成树问题2(Krukal算法)
O(|E|long|V|)
struct edge{
int u,v,cost;
};
bool cmp(const edge& a,const edge& b){
return a.cost < b.cost;
}
edge E[maxn];
int father[maxn];
int findfather(int x)
{
if(x!=father[x]){
father[x] = findfather(father[x]);
}
return father[x];
}
int Kruskal(int n,int m)
{
//ans为所求边权之和,Num_Edge为当前生成树的边数
int ans = 0,Num_Edge = 0;
for(int i=1;i<=n;i++)
father[i] = i;
sort(E+1,E+m+1,cmp);
for(int i=1;i<=m;i++){
int faU = findfather(E[i].u);
int faV = findfather(E[i].v);
if(faU!=faV){
father[faU] = faV;
ans += E[i].cost;
Num_Edge++;
if(Num_Edge==n-1) break;
}
}
if(Num_Edge!=n-1) return -1;
else return ans;
}