题目15.1链接All Pairs Shortest Path
注意点:
方法采用弗洛伊德算法,判断负环的方法是算法执行完后如果一个顶点到自身的距离为负,则代表图中有负环。
算法思路是采用了动态规划思想,Ak[i,j]代表经过了k个中间点的从点i到点j的路径长度,可知有
Ak[i,j] = min(Ak-1[i,j], Ak-1[i, k] + Ak-1[k, j]),
即对于对于是否经过第k个中间点,取其路径最短的情况。
且,有Ak[i, k] = min(Ak-1[i, k], Ak-1[i, k] + Ak-1[k, k]) = Ak-1[i, k],可知可以把动态规划的式子转化为:
A[i,j] = min(A[i,j], A[i, k] + A[k, j])。
#include
using namespace std;
const int MAX = 200;
const long long INF = (1LL<<32);
int V, E, s, t, d;
long long G[MAX][MAX];
void floyd()
{
for(int k=0; k<V; k++)
{
for(int i=0; i<V; i++)
{
if(G[i][k]==INF) continue;//防止得到的G的值大于INF,这种情况G[i][j]值不变
for(int j=0; j<V; j++)
{
if(G[k][j]==INF) continue;//防止得到的G的值大于INF,这种情况G[i][j]值不变
G[i][j] = min(G[i][j], G[i][k]+G[k][j]);
}
}
}
}
bool negative()//判断是否有负环
{
for(int i=0; i<V; i++)
{
if(G[i][i]<0)
return 1;
}
return 0;
}
int main()
{
cin >> V >> E;
for(int i=0; i<V; i++)//初始化
{
for(int j=0; j<V; j++)
{
if(i==j) G[i][j] = 0;
else G[i][j] = INF;
}
}
for(int i=0; i<E; i++)
{
cin >> s >> t >> d;
G[s][t] = d;
}
floyd();
if(negative())
cout << "NEGATIVE CYCLE" << endl;
else
{
for(int i=0; i<V; i++)
{
for(int j=0; j<V; j++)
{
if(j) cout << " ";
if(G[i][j]!=INF)
cout << G[i][j];
else
cout << "INF";
}
cout << endl;
}
}
return 0;
}
题目15.2链接Topological Sort
方法一:
思路是用队列S存入度为0的结点,每从队列中删除一个结点,就更新剩余未入队列的结点的入度,如果结点入度为0,则入队列,直到队列为空,排序完成。
#include
#include
#include
#include
#include
using namespace std;
const int MAX = 10005;
int V, E, s, t;
int indeg[MAX];
vector<int> G[MAX];//用于邻接表存储图
queue<int> S;//用于记录入度为0且未删除的结点
void load(int u)//用于每删除一个结点,更新其余结点的入度值
{
for(int i=0; i<G[u].size(); i++)
{
if(!indeg[G[u][i]]) continue;
indeg[G[u][i]]--;
if(!indeg[G[u][i]])
{
S.push(G[u][i]);
}
}
}
void topologicalSort()//用队列实现拓扑排列
{
int u;
while(!S.empty())
{
u = S.front(); S.pop();
printf("%d\n", u);
load(u);//将u的邻接点的入度减一,若为0则入队列
}
}
int main()
{
memset(indeg, 0, sizeof(indeg));
scanf("%d %d", &V, &E);
for(int i=0; i<E; i++)
{
scanf("%d %d", &s, &t);
indeg[t]++;
G[s].push_back(t);
}
for(int i=0; i<V; i++)//初始入队列
{
if(!indeg[i])
S.push(i);
}
topologicalSort();
return 0;
}
方法二:利用BFS使得每一次从一个入度为0的顶点出发,BFS遍历一遍,将遍历过的所有符号条件入度为0的存起来,当从所有结点均BFS一遍后一定保证将所有的边均遍历到,将所有的结果都考虑到。
复杂度为O(|V|+|E|)。
#include
#include
#include
#include
#include
using namespace std;
const int MAX = 10005;
const int INF = (1<<29);
int V, E, s, t, color[MAX];
int indeg[MAX];
vector<int> G[MAX];//用于邻接表存储图
queue<int> S;//用于记录入度为0且未删除的结点
void bfs(int s)//用于每删除一个结点,更新其余结点的入度值
{
queue<int> q;
q.push(s);
color[s] = 1;
while(!q.empty())
{
int u = q.front(); q.pop();
S.push(u);
for(int i=0; i<G[u].size(); i++)
{
int v = G[u][i];
indeg[v]--;
if(indeg[v]==0 && !color[v])//入度为0且未访问过
{
color[v] = 1;
q.push(v);
}
}
}
}
void topologicalSort()//用队列实现拓扑排列
{
for(int u=0; u<V; u++)
{
if(indeg[u]==0 && !color[u])//入度为0且未访问过
bfs(u);
}
while(!S.empty())
{
printf("%d\n", S.front());
S.pop();
}
}
int main()
{
memset(indeg, 0, sizeof(indeg));
memset(color, 0, sizeof(color));
scanf("%d %d", &V, &E);
for(int i=0; i<E; i++)
{
scanf("%d %d", &s, &t);
indeg[t]++;
G[s].push_back(t);
}
topologicalSort();
return 0;
}