
无向图中,给出每个点的点权,每个边的边权(即两点之间距离),要使起点到终点距离最短,求最短路径数,并求出最大点权之和。
单源最短路径问题。采用 Dijkstra算法。
该算法的思想为,设置数组
d
d
d ,代表源点与各顶点的最短距离。经过
n
n
n (点数)次遍历,每次遍历找到当前与源点
s
s
s 距离最短的顶点
u
u
u ,并以该顶点
u
u
u 为中介点,找到其经过并未访问的结点
v
v
v ,判断其能否使
d
[
v
]
d[v]
d[v] 更优(即
l
e
n
g
t
h
[
s
−
>
u
]
+
d
[
u
]
<
d
[
v
]
length[s->u]+d[u]
length[s->u]+d[u]时,
n
u
m
[
v
]
num[v]
num[v] 更新为
n
u
m
[
u
]
num[u]
num[u] ,
w
m
[
v
]
wm[v]
wm[v] 更新为
w
m
[
u
]
+
w
[
v
]
wm[u]+w[v]
wm[u]+w[v] 。 length[s->u]+d[u]=d[v]时,
n
u
m
[
v
]
num[v]
num[v] 更新为
n
u
m
[
u
]
+
n
u
m
[
v
]
num[u] + num[v]
num[u]+num[v] 。而只有
w
m
[
u
]
+
w
[
v
]
>
w
m
[
v
]
wm[u]+w[v]>wm[v]
wm[u]+w[v]>wm[v] 时(以
u
u
u 为中介使
w
m
wm
wm 更优),才更新
w
m
[
v
]
wm[v]
wm[v] 。一直想整理以下最短路相关的代码,今天不就来机会了吗。在写这道题前,我做了最短路的整理。
这题就是典型的 Dijkstra算法,那么先上 Dijkstra的板子。
Dijkstra板子#include
#include
#include
using namespace std;
const long long inf=0x7fffffff;;
int n,m,s,cnt;
int head[101000],vis[101000],dis[101000];
struct Edge
{
int v,w,next;
} edge[501000];
void addEdge(int u, int v,int w)
{
edge[++cnt].v=v;
edge[cnt].w=w;
edge[cnt].next=head[u];
head[u]=cnt;
}
struct node
{
int dis;
int pos;
bool operator <( const node &x )const
{
return x.dis < dis;
}
};
priority_queue<node>q;
void dijkstra()
{
for(int i=1; i<=n; i++)
{
dis[i]=inf;
vis[i]=0;
}
q.push((node)
{
0,s
});
dis[s]=0;
while(!q.empty())
{
node u=q.top();
q.pop();
int pos=u.pos;
if(vis[pos])
continue;
vis[pos]=1;
for(int i=head[pos]; i; i=edge[i].next)
{
int v=edge[i].v;
if(dis[v]>dis[pos]+edge[i].w)
{
dis[v]=dis[pos]+edge[i].w;
if(!vis[v])
q.push((node)
{
dis[v],v
});
}
}
}
}
int main()
{
scanf("%d%d%d",&n,&m,&s);
int u,v,w;
cnt=0;
for(int i=0; i<m; i++)
{
scanf("%d%d%d",&u,&v,&w);
addEdge(u,v,w);
}
dijkstra();
for(int i=1; i<=n; i++)
{
cout<<dis[i]<<" ";
}
return 0;
}
SPFA板子#include
const long long inf=2147483647;
const int maxn=10005;
const int maxm=500005;
using namespace std;
int n,m,s,num_edge=0;
int dis[maxn],vis[maxn],head[maxm];
struct Edge
{
int next,to,dis;
} edge[maxm]; //结构体表示静态邻接表
void addedge(int from,int to,int dis) //邻接表建图
{
//以下是数据结构书上的标准代码,不懂翻书看解释
edge[++num_edge].next=head[from]; //链式存储下一条出边
edge[num_edge].to=to; //当前节点编号
edge[num_edge].dis=dis; //本条边的距离
head[from]=num_edge; //记录下一次的出边情况
}
void spfa()
{
queue<int> q; //spfa用队列,这里用了STL的标准队列
for(int i=1; i<=n; i++)
{
dis[i]=inf; //带权图初始化
vis[i]=0; //记录点i是否在队列中,同dijkstra算法中的visited数组
}
q.push(s);
dis[s]=0;
vis[s]=1; //第一个顶点入队,进行标记
while(!q.empty())
{
int u=q.front(); //取出队首
q.pop();
vis[u]=0; //出队标记
for(int i=head[u]; i; i=edge[i].next) //邻接表遍历,不多解释了(也可用vector代替)
{
int v=edge[i].to;
if(dis[v]>dis[u]+edge[i].dis) //如果有最短路就更改
{
dis[v]=dis[u]+edge[i].dis;
if(vis[v]==0) //未入队则入队
{
vis[v]=1; //标记入队
q.push(v);
}
}
}
}
}
int main()
{
cin>>n>>m>>s;
for(int i=1; i<=m; i++)
{
int f,g,w;
cin>>f>>g>>w;
addedge(f,g,w); //建图,有向图连一次边就可以了
}
spfa(); //开始跑spfa
for(int i=1; i<=n; i++)
if(s==i) cout<<0<<" "; //如果是回到自己,直接输出0
else cout<<dis[i]<<" "; //否则打印最短距离
return 0;
} //结束
Floyd板子void Floyd() { //用FLoyd算法求有向网G中各对顶点和j之间的最短路径
for(int i=0; i<n; i++) { // 初始化
for(int j=0; j<n; j++) {
if(i==j) {
dist[i][j]=0;//自已到自己最短距离为0
} else {
dist[i][j]=G[i][j];
}
if(dist[i][j]<INF&&i!=j) {
p[i][j]=i; // 如果i和j之间有边,则将j的前驱置为i
} else {
p[i][j]=-1; //如果i和j之间无边,则将j的前驱置为-1
}
}
for(int k=0; k<n; k++) {
for(int i=0; i<n; i++) {
for(int j=0; j<n; j++) {
if(dist[i][k]+dist[k][j]<dist[i][j]) { //从i经k到j的- -条路径更短
dist[i][j]=dist[i][k]+dist[k][j]; //更新dist[i][j]
p[i][j]=p[k][j]; //更改的前驱
}
}
}
}
}
}
void DisplayPath(int s,int t) { //输出s到t的最短路径
if(p[s][t]!=-1) {
DisplayPath(s,p[s][t]);
cout<<p[s][t]<<"-->";
}
}
#include
#include
#include
using namespace std;
struct node {
int v, dis; //目标顶点和边权
};
const int maxv = 510, INF = 1000000000;
vector<node> Adj[maxv];
int d[maxv]; //源点到各顶点最短路径长度
int num[maxv]; //最短路径条数
int wm[maxv]; //最多救援队数
int w[maxv]; //各点救援队数
bool vis[maxv] = {false};
int n, m; //城市数 路径数
int s, e; //起点和终点
void Dijkstra(int s) {
fill(d, d + maxv, INF);
fill(num, num + maxv, 0);
fill(wm, wm + maxv, 0);
d[s] = 0;
num[s] = 1;
wm[s] = w[s];
for(int i = 0; i < n; i++) {
int u = -1, min = INF;
for(int j = 0; j < n; j++) { //找到最小d[u]
if(vis[j] == false && d[j] < min) {
u = j;
min = d[u];
}
}
if(u == -1) return;
vis[u] = true;
for(int j = 0; j < Adj[u].size(); j++) {
int v = Adj[u][j].v; //u能到达的顶点
if(vis[v] == false) {
if(d[v] > d[u] + Adj[u][j].dis) {
//以u为中介点使d[v]更优
d[v] = d[u] + Adj[u][j].dis;
num[v] = num[u];
wm[v] = wm[u] + w[v];
} else if(d[v] == d[u] + Adj[u][j].dis) {
num[v] += num[u];
if(wm[v] < wm[u] + w[v])
wm[v] = wm[u] + w[v];
}
}
}
}
}
int main() {
cin>>n>>m>>s>>e;
for(int i = 0; i < n; i++)
cin>>w[i];
int c1, c2, l;
node tmp;
for(int i = 0; i < m; i++) {
// 注意:无向图,两个方向都要输入
cin>>c1>>c2>>l;
tmp.v = c2;
tmp.dis = l;
Adj[c1].push_back(tmp);
tmp.v = c1;
Adj[c2].push_back(tmp);
}
Dijkstra(s);
cout<<num[e]<<" "<<wm[e];
return 0;
}