决定刷PAT,已经有一段时间,因为各种事情耽搁,就没能空下来,当发现是最后一次必须报名的时候,只有一个月的时间了…索性还不算太晚,这里整一下别人的PAT资源和自己的目录。
1.Dijkstra算法思路
假设它的起点是 a ,要求它到各点的最短距离
Dijkstra 思路是维护一个集合 s ,集合内的点是已经确定最短路的点,,每次操作找出距离这个集合最近的点加入集合中,将所有未访问点到该点的距离进行更新,松弛边长,存在 dis 中。
初始化阶段:将dis里均置为INF,并将出发点dis置为0,以便于直接进入循环
该算法的核心为三个步骤
- 1.找未被访问且距离集合最短的点
- 2.将该点加入集合
- 3.将所有未访问点到该点的距离进行更新,松弛边长
算法需要的数据类型
dis[ ] :记录到该点的最短路径长度
vis[ ] : 标记该点是否已经被访问,即是否在集合S中
伪代码如下:
//初始化
memset(v, 0, sizeof(v));
for(int i = 0; i < n; i++) dis[i] = (i==0 ? 0 : INF);
for(int i = 0; i < n; i++) {
int x, min = INF;
//1.如果y没有被加入集合,且d[y]是最小的,则把y加入集合且x = y
for(int y = 0; y < n; y++)
if(!vis[y] && dis[y] <= min) min = dis[y], x = y;
//2.新的点加入集合(这是更新之后的新x)
vis[x]=true;
//3.更新x相邻的点的d[i],实际上这里更新的是所有点,但是与x未相邻的w[x][y]值是无穷大,不可能被更新
for(int y = 0; y < n; y++)
dis[y] = min(dis[y], dis[x] + w[x][y]);
}
为什么仅需要将start点距离初始为0?
不那么做的时候,进入dij循环时,由于所有的dis[]
均为初始化inf
,这会导致第一个进入的为第一个inf
点,这不符合我们的预期,而将点初始化为0,可以在循环时,首先进入该点,进行操作,完美初始化。
2.dijkstra算法提高以及对应模板:
该算法的考察一般都不会只考一个参考标准,一般存在点权,边权和求最短路径的条数。在使用模版时,仅修改对应的第三步,前两步不变。
2.1存在点权和边权,求最短路径下最小花费:(求解模板一致)
将判断的条件进行修改即可。
int cost[MAXV][MAXV], c[MAXV];
fill(c, c + MAXV, INF);
c[s] = 0;
for (int v = 0; v < n; v++){
#当选择点到循环到的点可达且循环点未被访问过(即未加入集合)
if (!vis[v] && G[u][v] != INF){
#如果距离一致,不需要更新边,则取点权或边权更小的,将更小的记录
if (d[u] + G[u][v] == d[v] && c[u] + cost[u][v] < c[v]){
c[v] = c[u] + cost[u][v];
}
else if (d[u] + G[u][v] < d[v]){
d[v] = d[u] + G[u][v];
c[v] = c[u] + cost[u][v];
}
}
}
2.2求最短路径条数:
int num[MAXV] = {0};
num[s] = 1;
for (int v = 0; v < n; v++){
if (!vis[v] && G[u][v] != INF){
if (d[u] + G[u][v] == d[v]{
num[v] += num[u];
}
else if (d[u] + G[u][v] < d[v]){
d[v] = d[u] + G[u][v];
num[v] = num[u];
}
}
}
3. dijkstra PAT 甲级1003
这里给出柳神的代码和解析
#include <stdio.h>
#include <iostream>
#include <algorithm>
using namespace std;
int n, m, c1, c2;
int e[510][510], weight[510], dis[510], num[510], w[510];
bool visit[510];
const int inf = 99999999;
int main() {
scanf("%d%d%d%d", &n, &m, &c1, &c2);
for(int i = 0; i < n; i++)
scanf("%d", &weight[i]);
fill(e[0], e[0] + 510 * 510, inf);
fill(dis, dis + 510, inf);
int a, b, c;
for(int i = 0; i < m; i++) {
scanf("%d%d%d", &a, &b, &c);
e[a][b] = e[b][a] = c;
}
#必须要有个值首先进入,这里初始点为0点,将距离为0,进入循环时该点为第一个被考察。
dis[c1] = 0;
w[c1] = weight[c1];
num[c1] = 1;
for(int i = 0; i < n; i++) {
int u = -1, minn = inf;
for(int j = 0; j < n; j++) {
if(visit[j] == false && dis[j] < minn) {
u = j;
minn = dis[j];
}
}
if(u == -1) break;
visit[u] = true;
for(int v = 0; v < n; v++) {
if(visit[v] == false && e[u][v] != inf) {
if(dis[u] + e[u][v] < dis[v]) {
dis[v] = dis[u] + e[u][v];
num[v] = num[u];
w[v] = w[u] + weight[v];
//这问题里:即给了权重,又要求条数,所以需要w[],num[]两个数组
} else if(dis[u] + e[u][v] == dis[v]) {
num[v] = num[v] + num[u];
if(w[u] + weight[v] > w[v])
w[v] = w[u] + weight[v];
}
}
}
}
printf("%d %d", num[c2], w[c2]);
return 0;
}
4.PAT里其他题的代码:
PAT 甲级1018
#include<cstdio>
#include<vector>
#include<iostream>
#include<cmath>
using namespace std;
const int inf=99999999;
int cmax,n,sp,m,back1,need1,backmin,needmin;
int e[510][510],dis[510],weight[510];
vector<int> path, temppath, pre[510];
bool vis[510];
void djstra()
{
dis[0] = 0;
for(int j=0; j<=n; j++)
{
//step 1;
int mini = -1;
int minv = inf;
for(int i=0; i<=n; i++)
{
if(vis[i]==0 && dis[i]<minv)
{
minv = dis[i];
mini = i;
}
}
//step 2;
vis[mini] = 1;
for(int i=0; i<=n;i++)
{
//没有这一判断也能过,为最优子集并且不可达本身不会是最小的
if(vis[i]==0 && e[mini][i]!= inf)
{
if(dis[i] > e[mini][i]+ dis[mini])
{
dis[i] = e[mini][i]+ dis[mini];
pre[i].clear();
pre[i].push_back(mini);
}
else if(dis[i] == e[mini][i]+ dis[mini])
{
pre[i].push_back(mini);
}
}
}
}
//printf("%d", pre[sp][0]);
}
void dfs(int v)
{
temppath.push_back(v);
if(v ==0)
{
back1=need1=0;
for(int i=temppath.size()-1; i>=0;i--)
{
if(back1+weight[temppath[i]] > 0)
{
back1+=weight[temppath[i]];
}
else //if(abs(back1+weight[temppath[i]]) > need1 )
{
need1-= back1+weight[temppath[i]];
back1=0;
}
}
if(need1 < needmin)
{
needmin = need1;
backmin = back1;
path = temppath;
}
if(need1 == needmin && back1< backmin)
{
needmin = need1;
backmin = back1;
path = temppath;
}
temppath.pop_back();
return;
}
//这一步应该是遍历它的所有叶子节点
for(int i=0; i<pre[v].size(); i++)
dfs(pre[v][i]);
temppath.pop_back();
}
int main()
{
//init
fill(e[0], e[0]+510*510, inf);
fill(dis,dis+510,inf);
fill(vis,vis+510,0);
backmin = needmin = inf;
scanf("%d%d%d%d", &cmax, &n, &sp, &m);
for(int i=1;i<=n;i++)
{
scanf("%d", &weight[i]);
weight[i]-= cmax/2;
}
for(int i=0;i<m;i++)
{
int a,b;
scanf("%d%d", &a, &b);
scanf("%d", &e[a][b]);
e[b][a]= e[a][b];
}
//djstra+dfs
djstra();
dfs(sp);
printf("%d 0", needmin);
for(int i=path.size()-2; i>=0;i--)
printf("->%d",path[i]);
printf(" %d",backmin);
return 0;
}
PAT甲级 1030
#include<cstdio>
#include<vector>
#include<iostream>
using namespace std;
const int inf = 99999999;
int cost[510][510],e[510][510],dis[510];
int n,m,s,d,costmin,dismin;
bool vis[510];
vector<int> pre[510],temppath,path;
void djistra()
{
//init
fill(dis, dis+510, inf);
dis[s] = 0;
for(int i=0; i<n;i++)
{
//step 1 find shortest point
int mini=-1;
int minv=inf;
for(int j=0;j<n;j++)
{
if(vis[j]==false && minv > dis[j])
{
mini = j;
minv = dis[j];
}
}
//step 2 update not include edge
vis[mini]= true;
for(int v=0;v<n;v++)
{
if(vis[v]==false && e[mini][v]!=inf)
{
if(e[mini][v]+dis[mini]<dis[v])
{
dis[v] = e[mini][v]+dis[mini];
pre[v].clear();
pre[v].push_back(mini);
}
else if(e[mini][v]+dis[mini]==dis[v])
{
pre[v].push_back(mini);
}
}
}
}
dismin=dis[d];
}
void dfs(int v)
{
temppath.push_back(v);
if(v == s)
{
int costv = 0;
//this issue point algorithm
for(int j =1; j<temppath.size();j++)
{
int id1=temppath[j];
int id2=temppath[j-1];
costv +=cost[id1][id2];
}
if(costv <costmin)
{
costmin = costv;
path = temppath;
}
temppath.pop_back();
return ;
}
for(int i=0;i<pre[v].size();i++)
dfs(pre[v][i]);
temppath.pop_back();
}
int main()
{
//init
fill(cost[0], cost[0]+510*510, inf);
fill(e[0], e[0]+510*510, inf);
costmin = inf;
scanf("%d%d%d%d", &n, &m, &s, &d);
int a,b;
for(int i=0;i<m;i++)
{
scanf("%d%d",&a, &b);
scanf("%d%d", &e[a][b], &cost[a][b]);
e[b][a]= e[a][b];
cost[b][a]= cost[a][b];
}
//printf("cos %d",cost[0][1]);
djistra();
dfs(d);
//output
for(int i= path.size()-1;i>=0; i--)
printf("%d ",path[i]);
printf("%d %d",dismin, costmin);
return 0;
}