[USACO06NOV] 路障 Roadblocks(次短路)

来源:互联网 发布:终极算法 txt 编辑:程序博客网 时间:2024/06/06 02:38

[USACO06NOV]路障Roadblocks
(来自Luogu)
题目描述

贝茜把家搬到了一个小农场,但她常常回到FJ的农场去拜访她的朋友。贝茜很喜欢路边的风景,不想那么快地结束她的旅途,于是她每次回农场,都会选择第二短的路径,而不象我们所习惯的那样,选择最短路。 贝茜所在的乡村有R(1<=R<=100,000)条双向道路,每条路都联结了所有的N(1<=N<=5000)个农场中的某两个。贝茜居住在农场1,她的朋友们居住在农场N(即贝茜每次旅行的目的地)。 贝茜选择的第二短的路径中,可以包含任何一条在最短路中出现的道路,并且,一条路可以重复走多次。当然咯,第二短路的长度必须严格大于最短路(可能有多条)的长度,但它的长度必须不大于所有除最短路外的路径的长度。

数据规模
见题干

思路
这是一个次短路的题目,与一楼的那位同学的方法比较类似,都有用到最短路的思想,但是我操作更新短路的时候用的SPFA,而且原理似乎更简单易懂一些。下面讲讲本人的思路,开两个数组存最小值min1和(严格)次小值min2,这一点一定要注意,题干中说了,这里是严格的次小值!

首先是SPFA的老套路,初始化1节点的min1和min2为0,其它节点的min1和min2为MAXX。

然后就是比较特殊的更新操作:(假设从u到v权值为w)

第一种情况,if(min1[v]>min1[u]+w),当前最小值min1[v]赋值给次小值min2[v],更新min1[u]+w为最小值min1[v];队列中若无v元素,则把v放入队列。

第二种情况,if(min2[v]>min1[u]+w),更新min1[u]+w为次小值min2[v];队列中若无v元素,则把v放入队列。

第三种情况,if(min2[v]>min2[u]+w),更新min2[u]+w为 次小值min2[v];队列中若无v元素,则把v放入队列。

一定要按上述顺序进行判断哦。

还有最重要的一点,由于题干中说可以来回走,而初始化中我们把min1[1]=min2[1]=0了,所以在1节点第一次被弹出队列之后一定要把它恢复成MAXX,否则第4个点会被卡掉。

最后更新完,队列空了之后,输出min2[n]即可。

我感觉我说的应该比较详细了,大概就是这样吧。

接下来上代码(C++):

#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<algorithm>#include<queue>using namespace std;int i,j,m,n,s,temp;int y[200001],hd[5001],v[200001],nxt[200001];int min1[5001],min2[5001];int b[5001];queue<int>q;int r()//读入优化{    int aans=0,f=1;    char ch=getchar();    while(ch<'0'||ch>'9')    {        if(ch=='-')            f=-1;        ch=getchar();    }    while(ch>='0'&&ch<='9')    {        aans*=10;        aans+=ch-'0';        ch=getchar();    }    return aans*f;}void add(int xx,int yy,int zz)//建边{    y[++temp]=yy;    v[temp]=zz;    nxt[temp]=hd[xx];    hd[xx]=temp;}void spfa(){    int p,kk,kkk=0;    while(!q.empty())    {    if(b[1]==0&&!kkk)//1节点第一次弹出时,恢复1节点为MAXX    min1[1]=0x7f7f7f,min2[1]=0x7f7f7f,kkk=1;//标记上,表示已经恢复过    int x=q.front();    p=hd[x];    q.pop();    b[x]=0;    while(p)    {        if(min1[y[p]]>min1[x]+v[p])//上文所说的第一种情况        {            kk=min1[y[p]];//赋值min2,更新min1            min1[y[p]]=min1[x]+v[p];            min2[y[p]]=kk;            if(!b[y[p]])//对新节点进行判断,并放入队列            {                q.push(y[p]);                b[y[p]]=1;            }        }        if(min2[y[p]]>min1[x]+v[p])//第二种情况        {            if(min1[y[p]]!=min1[x]+v[p])//注意严格次短路            min2[y[p]]=min1[x]+v[p];            if(!b[y[p]])/对新节点进行判断,并放入队列            {                q.push(y[p]);                b[y[p]]=1;            }        }        if(min2[y[p]]>min2[x]+v[p])//第三种情况        {            if(min1[y[p]]!=min2[x]+v[p])//注意严格次短路            min2[y[p]]=min2[x]+v[p];            if(!b[y[p]])/对新节点进行判断,并放入队列            {                q.push(y[p]);                b[y[p]]=1;            }        }        p=nxt[p];    }}}int main(){    memset(min1,0x7f7f7f,sizeof(min1));//初始化    memset(min2,0x7f7f7f,sizeof(min2));    n=r(),m=r();    int xx,yy,zz;    for(i=1;i<=m;i++)    {        xx=r(),yy=r(),zz=r();        add(xx,yy,zz);        add(yy,xx,zz);    }    q.push(1);    b[1]=1;    min1[1]=0,min2[1]=0;    spfa();    cout<<min2[n];//输出}

这里写图片描述