ACM暑期集训5

今天主要学习力图论基础和最短路径

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<m; ++i){
            scanf("%d%d%d",&a,&b,&c);
            w[a][b] = w[b][a] = c;
        }

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)
{
    queue<int>q;
    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]<mi)
				mi=dis[j],pos=j;
		}
		vis[pos]=1;
		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为点的数目),每次遍历所有的边,进行松弛操作(对于<u,v>权值为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;i<n;i++)
    {
        for(int j=0;j<m;j++)
        {
        int u=e[j].from,v=e[j].to;
        if(dis[u]!=INF&&dis[v]>dis[u]+e[j].w)
              dis[v]=dis[u]+e[j].w  
        }
    }
    for(int i=0;i<m;i++)
    {
    int u=e[j].from,v=e[j].to;
        if(dis[u]!=INF&&dis[v]>dis[u]+e[j].w)
             return false; 
    }
    return true;

}

          4)SPFA算法

算法步骤:

(1)初始化:建立一个队列(queue),只将起点放入队列中          

dis[]除起点外都为INF,vis[]除起点外都为0

(2)从队列中弹出一个点,松弛所有与它直接相连能够松弛的点。如果松弛的点不在队列中,就将其压入队列。

(3)重复步骤(2)直到队列为空。

判断负环:如果存在节点入队的次数超过n次,那么就存在源点可达的负环。

void spfa(int s)
{
     queue<int>q;
     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)有不同的价格,同时在城市之间移动也有一定的花费。现在要在一座城市买一件货物,在另一座城市卖出去,问最多能够赚多少钱。

思路:

建立一个源点和一个汇点

 

猜你喜欢

转载自blog.csdn.net/qq_41383801/article/details/81210579