最短路问题
图里面比较经典的问题,这里总结几种求最短路径的方法,1.bellman-ford(贝尔曼), 2.floyd(弗洛伊德),3.dijkstra(迪杰斯特拉),loyd(弗洛伊德)比较好理解,但是很暴力,大一点的数据就会爆掉了。dijkstra(迪杰斯特拉)是一种贪心的算法在里面。
bellman-ford
1.用d数组存储从出发点到顶点i的最短距离。
2.d[ i ]=min{ d[ j ]+从j 到 i 的边的权值,d[ i ]};不断更新
struct{
int from;
int to;
int cost;
}g[maxn];
int d[maxn];
int n,m;
void bellman-ford(int s)
{
memset(d,inf,sizeof(d));
d[s]=0;
while(true)
{
bool update=false;
for(int i=0;i<m;i++)
{
if(d[g[i].from]!=inf&&d[g[i].to]>d[g[i].from]+g[i].cost)
{
d[g[i].to]=d[g[i].from]+g[i].cost;
update=true;
}
}
if(!update)
break;
}
}
3.还可以用来检查负环(负圈:总长度小于0的有向环路。)的存在。遍历整个图更新的时候,最短路不会经过一个点两次,即,最多执行while那个循环,n-1(点数减一次)。所以可以一开始令一个数组的初值为0。
bool find_furoot()
{
memset(d,0,sizeof(d));
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
{
if(d[g[j].to]>d[g[j].from]+g[j].cost)
{
d[g[j].to]=d[g[j].from]+g[j].cost ;
if(i==n-1)//如果最后一次还进行了更新,说明有负圈
return true;
}
}
}
return false;
}
floyed
把所有点,和边存入一个二维数组里面,三重循环找优解。
int maze[maxn][maxn];
memset(maze,inf,sizeof(maze));
for(int k=0;k<maxn;k++)
for(int i=0;i<maxn;i++)
for(int j=0;j<maxn;j++)
if(maxn[i][j]>maxn[i][k]+maze[k][j])
maxn[i][j]=maxn[i][k]+maze[k][j];
然后二维数组里面存的就是 i 到 j 的最短路。
dijkstra
1.需要一个maze [ ][ ] 二维数组来存下整个图
2.一个vis [ ] 数组来看是否访问过
3.一个dis[ ]更新来求最短路
int maze[maxn][maxn];
int vis[maxn];
int dis[amxn];
void init()
{
memset(maze,inf,sizeof(maze));
for(int i=0;i<maxn;i++)
{
maze[i][i]=0;
}
memset(dis,inf,sizeof(dis));
}
void dij(int n)
{
memset(vis,0,sizeof(vis));
for(int i=0;i<maxn;i++)
dis[i]=maze[n][i];//先把图上的信息更新在dis里面
vis[n]=1;//标记已经访问过n这个点
for(int k=0;k<maxn;k++)
{
int minn=inf,temp; //寻找最短的那一条路,并且把点的位置记录下来
for(int i=0;i<maxn;i++)
{
if(!vis[i]&&dis[i]<minn)
{
minn=dis[i];
temp=i;
}
}
vis[temp]=1;//比较,更新dis
for(int j=0;j<maxn;j++)
{
if(dis[j]>maze[temp][j]+dis[temp])
dis[j]=maze[temp][j]+dis[temp];
}
}
}
int main()
{
init();
//输入图的相关信息
dij();
printf("%d",maze[a][b]);
}
拓扑排序
做一件事情有先后条件,某一件事必须在做完另外某一件事情之后才可以做。但也有一种可能就是,成环了。所以一般这个函数我喜欢写成bool 类型的
举一个成环的例子:
a---->b 意味着先做a再做b ; b---->c 意味着先做b再做c ;意味着如果这个三件事可以好好做的话,那么一定先做a再做c,但是如果此时还有一个条件的话(c----->a)那么意味着三者成环,没有顺序可以完成这个。
实现步骤:
1.找一个入度(比这件事情要前面完成事情的数量)为0的点
2.删除这个点的同时把以这个点的所有出入点的度减一
3.直到不存在入度为0 的点为止
存图的方法:
1.邻接矩阵
2.邻接表(一个struct 加一个head数组存,用stl里面的vector)
HDU 2647 reward
邻接表实现的拓扑排序
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <string>
#include <cstring>
#include <queue>
#include <vector>
#include <map>
#include <set>
#define inf 0x3f3f3f3f
#define ll long long
#define maxn 10003
using namespace std;
int bi[maxn];
int head[maxn];
int sum[maxn];
int n,m,num=0,ans=0;
struct node{
int to;
int next;
}edge[maxn*2];
void init()
{
memset(head,-1,sizeof(head));
memset(bi,0,sizeof(bi));
memset(sum,0,sizeof(sum));
num=0;
ans=0;
}
void add()
{
int a,b;
scanf("%d%d",&b,&a);
edge[num].to=b;
edge[num].next=head[a];
head[a]=num++;
bi[b]++;
}
bool pt()
{
int cnt=0;
queue<int> que;
while(!que.empty())
que.pop();
for(int i=1;i<=n;i++)
{
if(bi[i]==0)
{
que.push(i);
sum[i]=888;
}
}
while(!que.empty())
{
int now=que.front();
que.pop();
cnt++;
bi[now]=-1;
ans+=sum[now];
for(int i=head[now];i!=-1;i=edge[i].next)
{
int d=edge[i].to;
if(--bi[d]==0)
{
que.push(d);
sum[d]=sum[now]+1;
}
}
}
if(cnt==n)
return true;
else
return false;
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
init();
for(int i=0;i<m;i++)
{
add();
}
if(pt())
printf("%d\n",ans);
else
printf("-1\n");
}
}