[USACO06NOV] 路障 Roadblocks(次短路)

[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
#include
#include
#include
#include
#include
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<//输出
}

[USACO06NOV] 路障 Roadblocks(次短路)_第1张图片

你可能感兴趣的:(#,USACO,#,最短路,图论)