今天主要学习力图论基础和最短路径
1.图论基础
1)邻接矩阵存图
W[i][j]表示以ij为顶点的边的权值
const int N=105, INF=9999999;
int dis[N], w[N][N],vis[N],n,m;
//邻接矩阵存图
for(int i=1; i<=n; ++i){
w[i][i] = INF;
for(int j=i+1; j<=n; ++j)
w[i][j] = w[j][i] = INF;
}
for(int i=0; i
2)邻接表存图
直接上模板
int head[maxn]; //head[i]表示i所连的第一条边的编号(最后输入的边的编号)
struct node
{
int to;
int w;
int next; //next 起点所连的下一条边的编号
}e[maxn]; //e[i]表示第i条边
//加边:新加了一条a到b权值为w的边,其下标为cnt( cnt是对边计数)
void(int a,int b,int w)
{
e[cnt].to=b;
e[cnt].w=w;
e[cnt].next=head[a]; //每次新加的边作为第一条边,由next倒序指向前面的边。
head[a]=cnt++; //这里cnt从0开始,head[]初始化为-1
}
遍历与节点a相连的所有的边:
for(int i = head[a] ; i != -1 ; i = e[i].next){}
3)图的遍历
void dfs(int u)
{
vis[u]=1;
/**/
for(int i = head[a] ; i != -1 ; i = e[i].next)
{
int v=e[i].to;
if(!vis[v])
{
/**/
dfs(v);
}
}
}
void bfs(int s)
{
queueq;
memset(vis,0,sizeof(vis));
q.push(s);
vis[s]=1;
while(!q.empty())
{
int u=q.front();
q.pop();
for(int i = head[a] ; i != -1 ; i = e[i].next)
{
int v=e[i].to;
if(!vis[v])
{
/**/
vis[v]=1;
q.push(v);
}
}
}
}
2.最短路径
1)Floyd算法
松弛所有可松弛的路径,枚举路径的起止点和中间点
void floyd()
{
for(int k=1;k<=n;k++)
{
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(dis[i][j]>dis[i][k]+dis[k][j])
dis[i][j] = dis[i][k] + dis[k][j];
}
}
}
}
接下来上题
每条边上有一个权值,从a到b找一条路,使得它在所有路径中,边权最大的边的权值最小。求这个最小的权值。
for(int k=1;k<=n;k++)
{
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
dis[i][j]=min(dis[i][j],max(dis[i][p],dis[p][j]));
}
}
每个城市有一个危险值,求从城市a到b,路径上所经过的其他城市危险值不超过x的最短路长度。
dis[k][i][j] -- 从i到j经过危险值前k小的城市的最短路径长度
2).Dijkstra算法
分析:
将所有的点分为两部分,已标记的和未标记的
标记之后指的是保存的路径已经是最短路径。
vis[i]-标记节点i dis[i]-起点到点i的最短距离
初始化:除起点之外dis[]设为inf
(1)在所有尚未标记的点中选出距离起点最近的点,标记这个点,并且在未标记的点中更新与这个点相邻的点的最短距离。
(2)重复步骤(1)直到所有的点都被标记,即所有的点保存的距离都是最短距离。
void dijkstra(int s)
{
for(int i=1;i<=n;i++)
dis[i]=INF;
memset(vis,0,sizeof(vis));
dis[s]=0;
for(int i=1;i<=n;i++)
{
int mi=INF,pos=0;
for(int j=1;j<=n;j++)
{
if(!vis[j]&&dis[j] dis[pos]+w[pos][j])
dis[j] = dis[pos]+w[pos][j];
}
}
}
最短路计数 求num[i]:起点到i的最短路条数
if(dis[j]>dis[k]+Map[k][j])
num[j]=num[k];
else if(dis[j]==dis[k]+Map[k][j])
num[j]+=num[k];
城市之间的路径各自有高度限制,求从起点到终点允许的最大高度,并求这时的最短距离。
思路:
二分查找满足条件的最大高度 ,每次求最短路
3)Bellman-Ford算法
如果存在权值为负值的边,Dijkstra算法就会失效。
解决这个问题的方法:Bellman-Ford算法
算法步骤:
(1)初始化(起点处dis[s]=0,其余点为INF)
(2)循环n-1次(n为点的数目),每次遍历所有的边,进行松弛操作(对于权值为w的边,看是否有dis[v]>dis[u]+w)
【一条路径最多经过n-1条边,每次循环可以保证至少松弛一层】
(3)再遍历一次所有的边,尝试进行松弛,如果仍然能进行松弛(存在dis[v]>dis[u]+w),则说明存在原点可达的负环
struct node
{
int from;
int to;
int w;
}e[maxn];
bool bellman_ford(int s)
{
for(int i=1;i<=n;i++)
dis[i]=INF;
dis[s]=0;
for(int i=1;idis[u]+e[j].w)
dis[v]=dis[u]+e[j].w
}
}
for(int i=0;idis[u]+e[j].w)
return false;
}
return true;
}
4)SPFA算法
算法步骤:
(1)初始化:建立一个队列(queue),只将起点放入队列中
dis[]除起点外都为INF,vis[]除起点外都为0
(2)从队列中弹出一个点,松弛所有与它直接相连能够松弛的点。如果松弛的点不在队列中,就将其压入队列。
(3)重复步骤(2)直到队列为空。
判断负环:如果存在节点入队的次数超过n次,那么就存在源点可达的负环。
void spfa(int s)
{
queueq;
for(int i=1;i<=n;i++) dis[i]=INF;
memset(vis,0,sizeof(vis));
vis[s]=1,dis[s]=0;
q.push(s);
while(!q.empty())
{
int u=q.front();
q.pop();
vis[u]=0;
for(int i=head[u];i!=-1;i=e[i].next)
{
int v=e[i].to;
if(dis[v]>dis[u]+e[i].w)
{
dis[v]=dis[u]+e[i].w;
if(!vis[v])
{
vis[v]=1;
q.push(v);
}
}
}
}
}
货物在n个城市中(n<=100000)有不同的价格,同时在城市之间移动也有一定的花费。现在要在一座城市买一件货物,在另一座城市卖出去,问最多能够赚多少钱。
思路:
建立一个源点和一个汇点