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