题目
洛谷3008 [USACO11JAN]道路和飞机Roads and Planes
题解
Dijkstra+拓扑排序+乱搞
省选题怎么可能考裸的SPFA?
题目中有一句话改变了这题的最优解法:“如果有一条航线可以从A_i到B_i,那么保证不可能通过一些道路和航线从B_i回到A_i。”
它不仅告诉我们没有负环,还说明可以用拓扑序,还说这张图就是几个大的用单向边连接的连通块。
所以我们的决策是,块内dijkstra,块外拓扑序处理。
接下来就是乱码一通了。
代码
这个是把拓扑与dij分开实现的:
#include<queue>
#include<vector>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef pair<int,int> pii;
const int maxn=25010,maxm=100010;
int t,r,p,s;
struct E{int y,c,next;bool v;}e[maxm*2];int len=0,last[maxn];
void ins(int x,int y,int c,bool v)
{
e[++len]=(E){y,c,last[x],v};last[x]=len;
}
vector<int> fir[maxn];
int tot=0,dep[maxn],in[maxn];
struct O{int y,next;}h[maxm*2];int ool=0,ola[maxn];
void add(int x,int y)
{
h[++ool]=(O){y,ola[x]};ola[x]=ool;
in[y]++;
}
void dfs(int x)
{
for(int k=last[x];k;k=e[k].next)
{
int y=e[k].y;
if(dep[y]!=0 && dep[y]!=dep[x]) fir[dep[y]].push_back(y),add(dep[x],dep[y]);
if(dep[y]) continue;
if(e[k].v) dep[y]=++tot,fir[dep[y]].push_back(y),add(dep[x],dep[y]);
else dep[y]=dep[x];
dfs(y);
}
}
int d[maxn];
priority_queue<pii,vector<pii>,greater<pii> > q;
void dijkstra(int sstt)
{
for(int i=0;i<fir[sstt].size();i++)//把这块中的点放进来,用vector实现
{
int x=fir[sstt][i];
q.push(make_pair(d[x],x));
}
while(!q.empty())
{
int dis=q.top().first,x=q.top().second;q.pop();
while(!q.empty() && dis>d[x]) dis=q.top().first,x=q.top().second,q.pop();
if(q.empty() && dis>d[x]) break;
for(int k=last[x];k;k=e[k].next)
{
int y=e[k].y;
if(d[y]>d[x]+e[k].c)
{
d[y]=d[x]+e[k].c;
if(dep[y]==sstt) q.push(make_pair(d[y],y));//debug 每次只是同块间做dij,不能跨块
}
}
}
}
int main()
{
scanf("%d%d%d%d",&t,&r,&p,&s);
for(int i=1;i<=r;i++)
{
int x,y,c;
scanf("%d%d%d",&x,&y,&c);
ins(x,y,c,0);ins(y,x,c,0);
}
for(int i=1;i<=p;i++)
{
int x,y,c;
scanf("%d%d%d",&x,&y,&c);//debug %d
ins(x,y,c,1);
}
fir[1].push_back(s);
dep[s]=++tot;dfs(s);
queue<int> qq;
qq.push(1);
memset(d,63,sizeof(d));d[s]=0;
for(int i=1;i<=tot;i++)
{
int x=qq.front();qq.pop();
dijkstra(x);
for(int k=ola[x];k;k=h[k].next)
{
int y=h[k].y;
in[y]--;
if(in[y]==0) qq.push(y);
}
}
for(int i=1;i<=t;i++)
if(dep[i]==0) puts("NO PATH");
else printf("%d\n",d[i]);
return 0;
}
这个是两者合在一起实现的:
#include<queue>
#include<vector>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef pair<int,int> pii;
const int maxn=25010,maxm=100010;
int t,r,p,s;
struct E{int y,c,next;bool v;}e[maxm*2];int len=0,last[maxn];
void ins(int x,int y,int c,bool v)
{
e[++len]=(E){y,c,last[x],v};last[x]=len;
}
struct O{int x,next;}h[maxn];int ool=0,ola[maxn];
void add(int x,int y)//把x加入y集合
{
h[++ool]=(O){x,ola[y]};ola[y]=ool;
}
int tot=0,dep[maxn],in[maxn];
void dfs(int x)
{
add(x,dep[x]);
for(int k=last[x];k;k=e[k].next)
{
int y=e[k].y;
if(dep[y]!=0 && dep[y]!=dep[x]) in[dep[y]]++;
if(dep[y]) continue;
if(e[k].v) dep[y]=++tot,in[dep[y]]++;
else dep[y]=dep[x];
dfs(y);
}
}
int d[maxn];bool vis[maxn];
int list[maxn];int head,tail;
priority_queue<pii,vector<pii>,greater<pii> > q;
void dijkstra(int sstt)
{
head=0;tail=0;
list[0]=1;
while(head<=tail)
{
int u=list[head++];
for(int k=ola[u];k;k=h[k].next) q.push(make_pair(d[h[k].x],h[k].x));
while(!q.empty())
{
int x=q.top().second;q.pop();
if(vis[x]) continue;
vis[x]=true;
for(int k=last[x];k;k=e[k].next)
{
int y=e[k].y;
if(dep[y]!=dep[x])
{
in[dep[y]]--;
if(in[dep[y]]==0) list[++tail]=dep[y];
}
if(d[y]>d[x]+e[k].c)
{
d[y]=d[x]+e[k].c;
if(dep[x]==dep[y]) q.push(make_pair(d[y],y));
}
}
}
}
}
int main()
{
scanf("%d%d%d%d",&t,&r,&p,&s);
for(int i=1;i<=r;i++)
{
int x,y,c;
scanf("%d%d%d",&x,&y,&c);
ins(x,y,c,0);ins(y,x,c,0);
}
for(int i=1;i<=p;i++)
{
int x,y,c;
scanf("%d%d%d",&x,&y,&c);
ins(x,y,c,1);
}
dep[s]=++tot;dfs(s);
memset(d,63,sizeof(d));d[s]=0;
dijkstra(s);
for(int i=1;i<=t;i++)
if(dep[i]==0) puts("NO PATH");
else printf("%d\n",d[i]);
return 0;
}