链式前向星(存边):
void addedge(int x, int y, int l)
{
edge[++cnt].l = l;
edge[cnt].t = y;
edge[cnt].next = head[x];
head[x] = cnt;
}
NC17511 公交线路(求最短路)
题目链接
迪杰斯特拉算法:
思想:
1、dijkstra算法是求一个点到其他所有点的最短距离,以下设该点为s,即求s点到其他所有点的最短距离
2、用一个dis数组来记s到所有节点到的最短距离,首先先将该距离初始化为最大值,对于dis[s] = 0,到自己本身的距离就为0。首先先将所有与s有连边先更新dis值
3、用一个vis数组来标记当前点是否有访问过,每次从所有节点中找dis值最小且未访问过的节点x,将其当作中间节点,标记为访问过,即该点到s的最小距离已经求出,然后利用该最小距离作为中间节点,更新每个节点的dis值。即计算dis[x] + ve[x][k] < dis[k](k为任意一个节点)出现,就更新
4、优化:可以利用优先队列即最小堆来减少复杂度,因此每次都要从所有节点中找出dis值最小且未访问过的节点,那么我们可以将所有值的dis值存入优先队列中,每次取出dis值最小的(即队首元素),判断是否访问过,访问过则取下一个。这样直接取元素会更快
5、如果求s-k的最短距离:dis[k]
完整代码:
# include <bits/stdc++.h>
using namespace std;
int n, m, s, t;
struct ty1{
int t, l, next;
}edge[20010];
struct ty2{
int x, dis;
bool operator < (const ty2 &a)const{
return dis>a.dis;
}
};
priority_queue<ty2> q;
int head[1010], dis[1010];
bool vis[1010];
int cnt = 100;
void addEdge(int x, int y, int l)
{
edge[++cnt].l = l;
edge[cnt].t = y;
edge[cnt].next = head[x];
head[x] = cnt;
}
int di(int s, int t)
{
memset(dis, 0x3f, sizeof(dis));
memset(vis, 0, sizeof(vis));
dis[s] = 0;
ty2 tmp;
tmp.dis = 0, tmp.x = s;
q.push(tmp);
while (!q.empty())
{
ty2 tmp = q.top();
q.pop();
// cout<<tmp.dis<<endl;
if (vis[tmp.x])
continue;
vis[tmp.x] = 1;
for (int i=head[tmp.x]; i!=-1; i=edge[i].next)
{
int y = edge[i].t;
if (vis[y]) continue;
if (edge[i].l+dis[tmp.x]<dis[y])
{
dis[y] = edge[i].l+dis[tmp.x];
ty2 tmp2;
tmp2.x = y, tmp2.dis = dis[y];
q.push(tmp2);
}
}
}
if (dis[t]>=0x3f3f3f3f)
return -1;
return dis[t];
}
int main()
{
cin>>n>>m>>s>>t;
memset(head, -1, sizeof(head));
for (int i=1; i<=m; i++)
{
int x, y, l;
cin>>x>>y>>l;
addEdge(x, y, l);
addEdge(y, x, l);
}
cout<<di(s, t)<<endl;
return 0;
}
Bellman-Ford算法
思想:
1、与迪杰斯特拉算法相似,求一点到所有其他点的最小距离,设该起点为s,dis数组表示s到所有其他点的最小距离,同样的先初始化为最大值,且dis[s] = 0,。
2、枚举每个节点i,再枚举每个中间节点j,一旦中间节点可以使其更新dis值就更新,即
dis[j] + ve[j][i] < dis[i] (即s到j的最小距离+j到i的距离<s到i的最小距离)就更新dis值
3、如何判断所有节点均已算出与s的最短距离,即一旦所有节点的距离都无法更新时,则说明s到其他所有节点的最小距离均已算出,无法再进行更新。
4、优化:用队列,我们每次没必要都将所有点的边都看一遍,用一个队列,里面存放所有要进行迭代的点(作为中间节点),首先是放着初始点s,然后将s能更新的所有的点都放入队列中(不能重复放入节点),这样更新直到队列为空为止
5、如何判断是否为重复入队的节点,用一个vis数组,入队了就将其设为1,出队就设为0,这样就可以避免重复
6、答案s-k的最短距离即为dis[k]
完整答案:
# include <bits/stdc++.h>
using namespace std;
struct ty1{
int t, l, next;
}edge[20010];
queue<int>q;
int cnt, n, m, s, t;
int head[10010], dis[1010];
bool vis[10100];
void addedge(int x, int y, int l)
{
edge[++cnt].l = l;
edge[cnt].t = y;
edge[cnt].next = head[x];
head[x] = cnt;
}
int spfa(int s, int t)
{
memset(vis, 0, sizeof(vis));
memset(dis, 0x3f, sizeof(dis));
vis[s] = 1;
dis[s] = 0;
q.push(s);
while (!q.empty())
{
int x = q.front();
q.pop();
vis[x] = 0;
for (int i=head[x]; i!=-1; i = edge[i].next)
{
int y = edge[i].t;
if (dis[x] + edge[i].l < dis[y])
{
dis[y] = dis[x] + edge[i].l;
if (!vis[y])
{
q.push(y);
vis[y] = 1;
}
}
}
}
if (dis[t] >= 0x3f3f3f3f)
return -1;
else
return dis[t];
}
int main()
{
memset(head, -1, sizeof(head));
cin>>n>>m>>s>>t;
for (int i=1; i<=m; i++)
{
int x, y, len;
cin>>x>>y>>len;
addedge(x, y, len);
addedge(y, x, len);
}
cout<<spfa(s, t);
return 0;
}
Floyd算法:
思想:
1、可以求两点间的最短距离,枚举两点,枚举中间节点
2、顺序:先枚举中间节点,再枚举两点
for (int k=1; k<=n; k++)
for (int i=1; i<=n; i++)
for (int j=1; j<=n; j++)
{
if ((i!=j) && (j!=k) && (i!=k))
{
if (f[i][k] + f[k][j] <= f[i][j])
{
f[i][j] = f[i][k] + f[k][j];
}
}
}
NC17509 挖沟(求最小生成树)
题目链接
Prim算法:
思想:
1、从根节点开始,不断加入最小边,且加入的边仍能为树
2、用优先队列(最小堆),用一个vis数组表示该点是否已经加入树中,将所有要更新的节点放入队列。首先标记该根节点加入树,且初始队列里放根节点所有连接的边,每次从队列中取最短的边,取出该边所连接的点,更新最小距离。如果该点未加入树中,将该点所连接的边放入队列中,并标记已经加入树中,继续更新。
3、最后返回该最小距离即可
完整代码:
# include <bits/stdc++.h>
using namespace std;
struct ty1{
int t, l, next;
}edge[500000*2+10];
struct ty2{
int x, len;
bool operator < (const ty2& a)const{
return len>a.len;
}
};
int n, m, cnt;
int head[100000+10], vis[100000+10];
void addEdge(int a, int b, int len)
{
edge[++cnt].l = len;
edge[cnt].t = b;
edge[cnt].next = head[a];
head[a] = cnt;
}
priority_queue<ty2> q;
int prim()
{
int ans = 0;
vis[1] = 1;
for (int i=head[1]; i!=-1; i=edge[i].next)
{
ty2 tmp;
tmp.len = edge[i].l;
tmp.x = edge[i].t;
q.push(tmp);
}
while (!q.empty())
{
ty2 tmp = q.top();
q.pop();
int x = tmp.x;
if (vis[x]) continue;
vis[x] = 1;
ans += tmp.len;
for (int i = head[x]; i!=-1; i = edge[i].next)
{
int x = edge[i].t;
if (vis[x]) continue;
ty2 tmp;
tmp.x = x, tmp.len = edge[i].l;
q.push(tmp);
}
}
return ans;
}
int main()
{
cin>>n>>m;
memset(head, -1, sizeof(head));
for (int i=1; i<=m; i++)
{
int x, y, len;
cin>>x>>y>>len;
addEdge(x, y, len);
addEdge(y, x, len);
}
cout<<prim();
return 0;
}
4、优化:没必要将该点连接的所有结点均放入队列中,我们直接找最小边放入即可。即用一个dis数组,来记当前点所有边的最小距离,每次比较每次更新即可
完整代码:
# include <bits/stdc++.h>
using namespace std;
struct ty1{
int t, l, next;
}edge[500000*2+10];
struct ty2{
int x, len;
bool operator < (const ty2& a)const{
return len>a.len;
}
};
int n, m, cnt;
int head[100000+10], vis[100000+10];
void addEdge(int a, int b, int len)
{
edge[++cnt].l = len;
edge[cnt].t = b;
edge[cnt].next = head[a];
head[a] = cnt;
}
priority_queue<ty2> q;
int dis[100000+10];
int prim()
{
int ans = 0;
vis[1] = 1;
memset(dis, 0x7f, sizeof(dis));
for (int i=head[1]; i!=-1; i=edge[i].next)
{
ty2 tmp;
tmp.len = edge[i].l;
tmp.x = edge[i].t;
dis[tmp.x] = tmp.len;
q.push(tmp);
}
while (!q.empty())
{
ty2 tmp = q.top();
q.pop();
int x = tmp.x;
if (vis[x]) continue;
vis[x] = 1;
ans += tmp.len;
for (int i = head[x]; i!=-1; i = edge[i].next)
{
int x = edge[i].t;
if (vis[x]) continue;
if (dis[x]>edge[i].l)
{
ty2 tmp;
dis[x] = edge[i].l;
tmp.x = x, tmp.len = edge[i].l;
q.push(tmp);
}
}
}
return ans;
}
int main()
{
cin>>n>>m;
memset(head, -1, sizeof(head));
for (int i=1; i<=m; i++)
{
int x, y, len;
cin>>x>>y>>len;
addEdge(x, y, len);
addEdge(y, x, len);
}
cout<<prim();
return 0;
}
Kruskal算法
思想:
1、将所有的边从小到大排序,贪心的选取最小边加入树中,并且保证该边加入后不会在树中形成环
2、如何判断是否可以加入树中,利用并查集,每次加入边,先判断其两点的父亲结点是否相同,相同说明加入该边则会形成环,不相同就将两点连边
完整代码:
# include <bits/stdc++.h>
using namespace std;
int n, m;
int fa[100000+10];
struct ty{
int x, y, z;
}t[500000+10];
bool cmp(ty t1, ty t2)
{
return t1.z<t2.z;
}
int find(int x)
{
return x == fa[x]? x: fa[x]=find(fa[x]);
}
int ans;
int main()
{
cin>>n>>m;
for (int i=1; i<=n; i++)
fa[i] = i;
for (int i=1; i<=m; i++)
{
int x, y, z;
cin>>x>>y>>z;
t[i].x = x;
t[i].y = y;
t[i].z = z;
}
sort(t+1, t+1+m, cmp);
for (int i=1; i<=m; i++)
{
int fx = find(t[i].x), fy = find(t[i].y);
if (fx!=fy)
{
ans += t[i].z;
fa[fx] = fy;
}
}
cout<<ans<<endl;
return 0;
}