题目链接
Dijkstra典型的贪心思想。
本题在Dijkstra算法上增加点权和统计最短路径条数,除了正常实现算法之外,尝试使用优先队列求解,以提高运行效率
但好像优先队列时间还没有正常实现快,不明白怎么回事,但学习一下优先队列实现的思路还是好的
效率上看:SPFA>优先队列>正常>Bellman(但在本题上优先队列速度略慢于正常Dijkstra)
#include
using namespace std;
const int maxn = 505;
const int inf = 0x3fffffff;
int N, M, C1, C2;
int team[maxn];//点权,各点救援队数目
int pathCnt[maxn];//最短路径个数
int G[maxn][maxn];//无向有权图
int dist[maxn], t[maxn];
bool vis[maxn];
int main(int argc, char *argv[]) {
scanf("%d%d%d%d", &N, &M, &C1, &C2);
for(int i = 0; i < N; ++i)
{
scanf("%d", &team[i]);
dist[i] = inf;
}
for(int i = 0; i < M; ++i)
{
int c1, c2, l;
scanf("%d%d%d", &c1, &c2, &l);
G[c1][c2] = G[c2][c1] = l;
}
//Dijkstra
dist[C1] = 0;//初始化
t[C1] = team[C1];
pathCnt[C1] = 1;
for(int i = 0; i < N; ++i)
{
int u, udist = inf;
for(int j = 0; j < N; ++j)//寻找当前最短路径
{
if(!vis[j])
{
if(dist[j] < udist)
{
u = j;
udist = dist[j];
}
}
}
vis[u] = true;
for(int v = 0; v < N; ++v)
{
if(!vis[v] && G[u][v])
{
if(udist + G[u][v] < dist[v])//更新
{
dist[v] = udist + G[u][v];//更新最短距离
t[v] = t[u] + team[v];//更新点权
pathCnt[v] = pathCnt[u];//更新路径数目
}
else if(udist + G[u][v] == dist[v])
{
if(t[u] + team[v] > t[v])
t[v] = t[u] + team[v];
pathCnt[v] += pathCnt[u];
}
}
}
}
printf("%d %d", pathCnt[C2], t[C2]);
return 0;
}
优先队列在Dijkstra中的作用,在O(1)的时间内得到dist[]中的最短距离的点,减少顺序循环遍历,以O(N)的时间在dist[]数组找最短距离的点。但中间对优先队列的插入删除同样会消耗O(logN)的时间
鉴于优先队列只是提供最短距离的点,因此不能用优先队列取代dist[]数组,因为无法找到相应顶点进行最短距离的更新操作。除此之外根据题意仍要有记录点权的数组和记录最短路径条数的数组。
若当前距离等于最短距离时,只需更新点权和最短路径条数,无需再将节点入队,否则只是重复访问该节点。
#include
using namespace std;
const int maxn = 505;
const int inf = 0x3fffffff;
int N, M, C1, C2;
int team[maxn];//点权,各点救援队数目
int pathCnt[maxn];//最短路径个数
int G[maxn][maxn];//无向有权图
int dist[maxn], t[maxn];//最短距离dist,最大队员数目t
bool vis[maxn];
struct Node{
int index;//顶点
int dist;//最短距离
Node(){}
Node(int id, int dd)
{
index = id;
dist = dd;
}
friend bool operator < (Node n1, Node n2)//重载运算符,在优先队列中排序
{
if(n1.dist != n2.dist)
return n1.dist > n2.dist;
else
return n1.index > n2.index;
}
}node;
priority_queue<Node> q;//优先队列便于查找最短距离
int main(int argc, char *argv[]) {
scanf("%d%d%d%d", &N, &M, &C1, &C2);
for(int i = 0; i < N; ++i)
{
scanf("%d", &team[i]);
dist[i] = inf;
}
for(int i = 0; i < M; ++i)
{
int c1, c2, l;
scanf("%d%d%d", &c1, &c2, &l);
G[c1][c2] = G[c2][c1] = l;
}
//初始化
dist[C1] = 0;
t[C1] = team[C1];
pathCnt[C1] = 1;
node = Node(C1, dist[C1]);
q.push(node);
//Dijkstra
while(!q.empty())
{
Node now = q.top();
q.pop();//删除队首元素
int u = now.index;
int udist = now.dist;
if(vis[u])//若在 udist + G[u][v] == dist[v]条件中q.push(node),队列中元素会重复
continue;
vis[u] = true;
for(int v = 0; v < N; ++v)
{
if(!vis[v] && G[u][v])
{
if(udist + G[u][v] < dist[v])
{
t[v] = t[u] + team[v];
pathCnt[v] = pathCnt[u];
dist[v] = udist + G[u][v];
node = Node(v, dist[v]);
q.push(node);//更新后的节点入队
}
else if(udist + G[u][v] == dist[v])
{
t[v] = max(t[v], t[u] + team[v]);
pathCnt[v] += pathCnt[u];
/*node = Node(v, dist[v]);//加上后需要在循环外判断是否被访问
q.push(node);//无需此入队操作,只会造成重复访问*/
}
}
}
}
printf("%d %d", pathCnt[C2], t[C2]);
return 0;
}
主要是练习一下Bellman算法,实现起来与Dijkstra算法类似,与Dijkstra算法不同之处:
#include
using namespace std;
const int maxn = 505;
const int inf = 0x3fffffff;
int N, M, C1, C2;
struct Node{
int v;//边的终点
int dist;//边权
Node(){}
Node(int vv, int dd){
v = vv;
dist = dd;
}
}node;
vector<Node> G[maxn];
set<int> pre[maxn];//前驱结点集合
int weight[maxn];
int dist[maxn], w[maxn], num[maxn];
void Bellman()
{
for(int i = 0; i < N - 1; ++i)//做N-1次遍历
{
for(int u = 0; u < N; ++u)//遍历图中每条边
{
for(int j = 0; j < G[u].size(); ++j)
{
int v = G[u][j].v;
int vdist = G[u][j].dist;
if(dist[u] + vdist < dist[v])
{
dist[v] = dist[u] + vdist;
w[v] = w[u] + weight[v];
num[v] = num[u];
pre[v].clear();
pre[v].insert(u);
}
else if(dist[u] + vdist == dist[v])
{
pre[v].insert(u);
w[v] = max(w[v], w[u] + weight[v]);
num[v] = 0;//重新统计num[v]
for(set<int>::iterator it = pre[v].begin(); it != pre[v].end(); ++it)
{
num[v] += num[*it];
}
}
}
}
}
}
int main(int argc, char *argv[]) {
scanf("%d%d%d%d", &N, &M, & C1, &C2);
for(int i = 0; i < N; ++i)
{
scanf("%d", &weight[i]);
dist[i] = inf;
}
for(int i = 0; i < M; ++i)
{
int c1, c2, l;
scanf("%d%d%d", &c1, &c2, &l);
node = Node(c2, l);
G[c1].push_back(node);//构造无向有权图
node = Node(c1, l);
G[c2].push_back(node);
}
dist[C1] = 0;
w[C1] = weight[C1];
num[C1] = 1;
Bellman();
printf("%d %d", num[C2], w[C2]);
return 0;
}
SPFA对Bellman算法优化之处在于一个递进关系:只有当某个顶点dist[u]值改变时,从它出发的边的邻接点v的dist[v]值才有可能改变。
SPFA算法的性能优于堆优化的Dijkstra算法
通过SPFA算法找出最短路径,存入pre[]。通过对pre[]进行dfs,计算得到相应的最大点权值和最短路径条数
#include
using namespace std;
const int maxn = 505;
const int inf = 0x3fffffff;
int N, M, C1, C2;
struct Node{
int v;//边的终点
int dist;//边权
Node(){}
Node(int vv, int dd){
v = vv;
dist = dd;
}
}node;
vector<Node> G[maxn];
vector<int> pre[maxn];//前驱结点集合
int weight[maxn];
int dist[maxn], w[maxn], num[maxn];
bool inq[maxn];
int inqcnt[maxn];//记录入队次数
void SPFA()
{
queue<int> q;
q.push(C1);
inq[C1] = true;
inqcnt[C1]++;
while(!q.empty())
{
int u = q.front();
q.pop();
inq[u] = false;//设置u不在队列中
for(int i = 0; i < G[u].size(); ++i)
{
int v = G[u][i].v;
int vdist = G[u][i].dist;
if(dist[u] + vdist < dist[v])
{
dist[v] = dist[u] + vdist;
// w[v] = w[u] + weight[v];//在SPFA过程中不能准确计算w[],num[]
// num[v] = num[u];
pre[v].clear();
pre[v].push_back(u);
if(!inq[v])
{
q.push(v);
inq[v] = true;
inqcnt[v]++;
}
}
else if(dist[u] + vdist == dist[v])
{
//w[v] = max(w[v], w[u] + weight[v]);
pre[v].push_back(u);
/* num[v] = 0;
for(vector::iterator it = pre[v].begin(); it != pre[v].end(); ++it)
num[v] += num[*it]; */
}
}
}
}
int pathcnt = 0, maxWeight = -1;
vector<int> tempPath;
void dfs(int v)
{
if(v == C1)
{
tempPath.push_back(v);
int curWeight = 0;
pathcnt++;
for(int i = tempPath.size() - 1; i >= 0; i--)
{
int id = tempPath[i];
curWeight += weight[id];
}
if(curWeight > maxWeight)
maxWeight = curWeight;
tempPath.pop_back();
return;
}
tempPath.push_back(v);
for(int i = 0; i < pre[v].size(); ++i)
{
dfs(pre[v][i]);
}
tempPath.pop_back();
}
int main(int argc, char *argv[]) {
scanf("%d%d%d%d", &N, &M, & C1, &C2);
for(int i = 0; i < N; ++i)
{
scanf("%d", &weight[i]);
dist[i] = inf;
}
for(int i = 0; i < M; ++i)
{
int c1, c2, l;
scanf("%d%d%d", &c1, &c2, &l);
node = Node(c2, l);
G[c1].push_back(node);//构造无向有权图
node = Node(c1, l);
G[c2].push_back(node);
}
dist[C1] = 0;
w[C1] = weight[C1];
num[C1] = 1;
SPFA();
dfs(C2);
printf("%d %d", pathcnt, maxWeight);
return 0;
}