图论 最短路#4 SPFA

题目链接,可TP


------------------------------------------------------------------------------------------------------

先介绍一下 SPFA:

SPFA算法是1994年西安交通大学  段凡丁 提出的。一种求单元最短路的算法。

算法中需要用到的主要变量

int n;//表示n个点,从1到n标号

int s,t;//s为起点,t为终点

int d[N]; //d[i]表示起点s到点i的最短路

int p[N]; //记录路径(或者说记录前驱)

queue q; //一个队列,用STL实现,当然可有手打队列

int vis[N]; // vis[i] = 1表示点i在队列中,vis[i]= 0 表示 不在队列中

------------------------------------------------------------------------------------------------------

几乎所有的最短路算法其步骤都可以分为两步:

1.初始化

d数组全部复制为MAX (无穷大),p数组全部为s (即起点),或者赋值为-1,表示还没有知道前驱,然后d[s] = 0;表示起点不用求最短路径,或者说最短路 就是0,将起点入队;

2.松弛操作

读取队头顶点u,并将队头顶点u出队(消除标记);将与点u相连的虽有点v进行松弛操作,如果更新估计值(即令d[v]变小),那么就更新,另外,如果点v没有在队列中,那么要将点v入队(标记),如果已经在队列中,不用入队。

以此循环,知道队列为空时,完成了单源最短路的求解。

------------------------------------------------------------------------------------------------------

SPFA可以处理负权边

定理:只要最短路径存在,上述SPFA算法必定能求出最小值。

证明:每次将点放入队尾,都是经过松弛操作达到的。换言之,每次的优化将会有某个点V的最短路径估计值d[V]变小。所以算法的执行会使d越来越小。由于我们假定图中不存在负权回路,所以每个结点都有最短路径值。因此,算法不会无限执行下去,随着d值的变小,直到到达最短路径值时,算法结束,这时的最短路径估计值就是对应结点的最短路径值。(证明完毕)

期望的时间复杂度O(ke),其中k味儿所有顶点进队的平均次数。可以证明k一般小于等于2.


判断有无负环:

如果某个点进入队列的次数超过N次则存在负环(SPFA无法处理带负环的图)


SPFA的两种写法,bfs 和 dfs ,bfs判别负环不稳定,相当于限深度搜索,但是设置得好的的话还是没没问题的,dfs的话判断负环还是很快的

------------------------------------------------------------------------

最短路#4 SPFA 题解

Problem A:
HUD 畅通工程续 1874
题目大意:
输入两个真鳄龟属N,M(0

这题的四种解法大全---->此处TP

思路:
按照上面介绍的思路一步一步来

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define MAX 99999999
using namespace std;
int mp[205][205];
int dis[205];
int vis[205];
int n,m;
int st,en;
void SPFA()
{
	int i;
	//初始化路线总和   &&  初始化标记
	for(i=0;i q;
	while(!q.empty())
		q.pop();
	//起始点标记
	dis[st]=0;
	vis[st]=1;
	q.push(st);
	
	while(!q.empty())
	{
		int t;
		t=q.front();
		q.pop();
		vis[t]=0;
		for(i=0;idis[t]+mp[t][i])
			{
				dis[i] = dis[t] + mp[t][i];
		//若没有走过,标记已经走过,并放入队列
				if( !vis[i])
				{
					vis[i]=1;
					q.push(i);
				}
			}
	}
}
int main()
{
	int u,v,cost;
	int i,j;
	while(cin>>n>>m)//n为点,m为边
	{
		//初始化邻接矩阵
		for(i=0;i>u>>v>>cost;
			if(cost>st>>en;
		//SPFA
		SPFA();
		//输出结果
		if(dis[en] < MAX)
			cout<


Problem B

POJ 1201 Intervals

题目大意: 
输入一个整数n,(1<=n<=50.000)n表示有n个区间,接下来n行,每行3个数ai,bi,ci(0<=ai<=bi<=50.000 and 1<= ci<=  bi-ai+1)在ai到bi中间 有ci个数要取出,求总共要去除多少数

思路:

百度了一下。此题需用:差分约束+spfa,思路看---->链接

百度大法好呀,百度大法好!



#include 
#include 
#include 
#include 
#include 
using namespace std;
const int M = 151010;
const int INF = -9999999;

struct Edge   //邻接表
{
    int v;
    int w;
    int next;  //
} edge[M];
int dis[M];
int head[M];  //保存边的头结点编号
bool visit[M];
int num,n,maxx,minn;
int number ;
queue p;
int SPFA()
{
    int e;
    for(int i = minn; i <= maxx; i++)
        dis[i] = INF;
    dis[minn] = 0;
    p.push(minn);//源点入队
    visit[minn] = true; //标记在队中
    while( !p.empty() )
    {
        e = p.front();
        p.pop();
        visit[e] = false; //标记不在队中
        for(int i = head[e]; i != -1; i = edge[i].next)
        {
            if (dis[edge[i].v] < dis[e] + edge[i].w)   //找最长路,三角不等式
            {
                dis[edge[i].v] = dis[e] + edge[i].w;
                if( !visit[edge[i].v] )
                {
                    visit[edge[i].v] = true;
                    p.push(edge[i].v);
                }
            }

        }

    }
    return dis[maxx];
}
int main()
{

    int a,b,c;
    num = 0;
    number = 1;
    maxx = -9999;
    minn = 9999;
    memset(dis,0,sizeof(dis));
    memset(head,-1,sizeof(head));  //不要写0啊啊啊啊
    memset(visit,false,sizeof(visit));
    cin >> n;
    for(int i = 1; i <= n; i++)
    {
        cin >> a >> b >> c;

        edge[num].next = head[a];
        edge[num].v = b + 1;
        edge[num].w = c;
        head[a] = num;
        num ++;
        if(maxx < b+1)
            maxx = b+1;
        if(minn > a)
            minn = a;

    }
    for(int i = minn; i < maxx; i++)
    {

        edge[num].next = head[i];
        edge[num].v = i+1;
        edge[num].w = 0;
        head[i] = num;
        num++;
        edge[num].next = head[i+1];
        edge[num].v = i;
        edge[num].w = -1;
        head[i+1] = num;
        num++;
    }
    int ans = SPFA();
    cout << ans <


Problem C

POJ 3169 Layout
题目大意:
有n头牛排成一条线,输入一个数字N(2<=N<=1,000),ML,MD(1<=ML<=10,000,1<=MD<=10,000)
接着以ML的形式,输入ML行
输A,B,D三个数(1<=A
接着以MD的形式,输入MD行
输A,B,D三个数(1<=A
若不存在最大距离(存在负环)输出-1,若距离可以任意 输出-2,否则输出最大距离
思路:
首先对于任一两头相邻的牛,它们之间的距离大于或等于0;其次,由ML个条件得到S[b]-S[a] <= d,
最后由MD个条件得到S[a]-S[b]<= -d.
百度大法好啊,百度大法好!

#include 
#include 
#include 
#include 
#include 
#define INF 0x3f3f3f3f
#define maxn  1100
using namespace std;

struct node {
	int u, v, w, next;
};

node edge[1100000];
int dist[maxn], head[maxn], cnt;
int used[maxn], n, ML, MD;
bool vis[maxn];
//初始化
void init()
{
	cnt = 0;
	memset(head, -1, sizeof(head));
	memset(used, 0, sizeof(used));
	memset(vis, 0 ,sizeof(vis));
}
void add(int u, int v, int w)
{
	edge[cnt].u=u; 
	edge[cnt].v=v;
	edge[cnt].w=w;
	edge[cnt].next=head[u];
	head[u] = cnt++;
}

//输入ML和MD的两种情况
void getmap()
{
	int a, b, d;
	for(int i = 1; i <= n; ++i)
		add(i + 1, i, 0);
	//增加最大距离
	while(ML--)
	{
		cin>>a>>b>>d;
		add(a, b, d);
	}
	//减掉最短距离
	while(MD--)
	{
		cin>>a>>b>>d;
		add(b, a, -d);
	}	
}
void SPFA(){
	for(int i = 1; i <= n; ++i)
		dist[i] = INF;
	dist[1] = 0;
	queueq;
	vis[1] = 1;
	used[1] = 1;
	q.push(1);
	while(!q.empty())
	{
		int u = q.front();
		q.pop();
		if(used[u] > n)
		{
			printf("-1\n");
			return ;
		}
		vis[u]  = 0;
		for(int i = head[u]; i != -1; i = edge[i].next)
		{
			int v = edge[i].v;
			int w = edge[i].w;
			if(dist[v] > dist[u] + w)
			{
				dist[v] = dist[u] + w;
				if(!vis[v])
				{
					vis[v] = 1;
					used[v]++;
					q.push(v);
				}
			}
		}
	}
	if(dist[n] == INF)
		cout<<"-2"<>n>>ML>>MD)
	{
		init();
		getmap();
		SPFA();
	}
	return 0;
}


Problem D

POJ 1511 Invitation Cards
题目大意:

输入N,N组测试,每一组测试输入P,Q两个数,(1<=P,Q<=1,000,000),最大数字为P,接下来有Q行,每一行a,b,w,在a,b之间的路线权为w,这是个有向图。

思路:

按照上面几道题的思路

百度大法好呀,百度大法好


#include 
#include 
#include 

#define nMax 1000010	
#define eMax 10000010
#define inf 1000000010

struct EDGE
{
	int v,w,next;
}edge[nMax], reEdge[nMax];

int nEdge[nMax], nReEdge[nMax];
int n, m, queue[eMax];
__int64 dis[nMax], sum;
bool vis[nMax];

void edgeInit()
{
	for (int i = 1; i <= n; ++ i)
	{
		nEdge[i] = 0;
		nReEdge[i] = 0;
	}
}

void spfaInit()
{
	for (int i = 1; i <= n; ++ i)
	{
		dis[i] = inf;
		vis[i] = false;
	}
}

void spfa(int * nEdge, EDGE * edge)
{
	spfaInit();
	int head = 0, tail = 1;
	dis[1] = 0;
	queue[0] = 1;

	while (head < tail)
	{
		int u = queue[head];
		vis[u] = true;
		int p = nEdge[u];
		while (p != 0)
		{
			int v = edge[p].v, w = edge[p].w;
			if (dis[v] > dis[u] + w)
			{
				dis[v] = dis[u] + w;
				if (!vis[v])
				{
					vis[v] = true;
					queue[tail] = v;
					tail ++;
				}
			}
			p = edge[p].next;
		}
		vis[u] = false;
		head ++;
	}
	for (int i = 1; i <= n; ++ i)
	{
		sum += dis[i];
	}
}

int main()
{
	int t;
	scanf("%d", &t);
	while (t --)
	{
		scanf("%d%d", &n, &m);
		edgeInit();
		for (int i = 1; i <= m; ++ i)
		{
			int u, v, w;
			scanf("%d%d%d", &u, &v, &w);
			edge[i].v = v;
			edge[i].w = w;
			edge[i].next = nEdge[u];
			nEdge[u] = i;

			reEdge[i].v = u;
			reEdge[i].w = w;
			reEdge[i].next = nReEdge[v];
			nReEdge[v] = i;
		}
		sum = 0;
		spfa(nEdge, edge);
		spfa(nReEdge, reEdge);
		printf("%I64d\n", sum);

	}
	return 0;
}


Problem EHEU 

最短路径问题 3790

题目大意:

输入两个整数n,m(1

思路:

总的思路差不多。然后主要是再多一个花费。

这题不百度不行啊!............

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
const int MAX = 1100;
const int INF = 0x3fffffff;
struct Node
{
    int len;
    int money;
};
Node map[MAX][MAX];
Node dis[MAX];
bool used[MAX];
Node SPFA(int start,int end,int n)
{
    int i;
	//初始化
    for(i=1;i<=n;i++)
    {
        dis[i].len = INF;
        dis[i].money = INF;
    }
	//起始位置
    dis[start].len = 0;
    dis[start].money = 0;
	//建立队列
    queue  q;
    q.push(start);
    used[start] = 1;
    while(!q.empty())
    {
        int mid;
        mid = q.front();
        q.pop();
        used[mid] = 0;
        for(i=1;i<=n;i++)
        {
			//松弛操作
            if(map[mid][i].len + dis[mid].len < dis[i].len)
            {
                dis[i].len = map[mid][i].len + dis[mid].len;
                dis[i].money = map[mid][i].money + dis[mid].money;
                if(!used[i])
                {
                    q.push(i);
                    used[i] = 1;
                }
            }
			//当最短距离有多条的时候,输出花费最少的
            if(map[mid][i].len + dis[mid].len == dis[i].len && dis[i].money > map[mid][i].money + dis[mid].money)
            {
                dis[i].money = map[mid][i].money + dis[mid].money;
            }
        }
    }
    return dis[end];
}
int main()
{
    int n,m;
    while(cin>>n>>m,n+m)
    {
        int i,j;
		//初始化邻接矩阵
        for(i=1;i>a>>b>>c>>d;
            if(c < map[a][b].len)
            {
                map[a][b].len = c;
                map[b][a].len = c;
                map[a][b].money = d;
                map[b][a].money = d;
            }
        }
		//输入起始点,和结束点
        int s,e;
        cin>>s>>e;
		//SPFA
        Node x = SPFA(s,e,n);
		//输出结果
        cout<





你可能感兴趣的:(图论)