图论基础
本部分包含的内容有:最短路,拓扑排序,最小生成树,树的直径和最近公共祖先,图的连通性等。
P1266速度限制
不难看出,这道题除了“有些道路没有速度限制”,就是一个裸的最短路。
我们可以用分层图的思想,将速度
于是
当前边有速度限制时:
当前边没有速度限制时:
void print(int x,int y){
if(!x){
printf("0 ");
return ;
}
print(pre[x][y],pre2[x][y]);
printf("%d ",x);
return ;
}
int main(){
scanf("%d%d%d",&n,&m,&d);
for(int i=1;i<=m;++i){
scanf("%d%d%lf%lf",&a,&b,&v,&l);
if(a==0&&!v)
v=70;
add(a,b,v,l);
}
for(int i=1;i<n;++i)
for(int j=0;j<=500;++j)
dis[i][j]=1e9;
q.push((node){0,0,0});
while(!q.empty()){
node fir=q.top();q.pop();
if(vis[fir.f][fir.v])
continue;
vis[fir.f][fir.v]=1;
for(int i=head[fir.f];i;i=nxt[i]){
if(wv[i]&&dis[ver[i]][(int)wv[i]]>dis[fir.f][fir.v]+wl[i]/wv[i]){
dis[ver[i]][(int)wv[i]]=dis[fir.f][fir.v]+wl[i]/wv[i];
if(!vis[ver[i]][(int)wv[i]]){
q.push((node){ver[i],wv[i],dis[ver[i]][(int)wv[i]]}),
pre[ver[i]][(int)wv[i]]=fir.f;
pre2[ver[i]][(int)wv[i]]=fir.v;
}
}
else if(!wv[i]&&dis[ver[i]][fir.v]>dis[fir.f][fir.v]+wl[i]/fir.v){
dis[ver[i]][fir.v]=dis[fir.f][fir.v]+wl[i]/fir.v;
if(!vis[ver[i]][fir.v]){
q.push((node){ver[i],fir.v,dis[ver[i]][fir.v]}),
pre[ver[i]][fir.v]=fir.f;
pre2[ver[i]][fir.v]=fir.v;
}
}
}
}
dis[x=0][y=0]=1e9;
for(int i=1;i<=500;++i)
if(dis[d][i]<dis[x][y])
x=d,y=i;
//cout<<dis[x][y]<<endl;
print(x,y);
return 0;
}
P2296 [NOIP2014 提高组] 寻找道路
因为起点可以是
int main(){
cin>>n>>m;
for(int i=1;i<=m;++i){
cin>>x>>y;
e1[x].push_back(y);//正向边
e2[y].push_back(x);//反向边
}cin>>fir>>end;
queue <int>q;
q.push(end);t[end]=1;
while(!q.empty()){
int xy=q.front();q.pop();
for(int i=e2[xy].size()-1;i>=0;--i)
if(t[e2[xy][i]]==0)
q.push(e2[xy][i]),
t[e2[xy][i]]=1;//从终点往前推,找出能够到终点的点
}
if(t[fir]==0)
{ cout<<"-1"<<endl;return 0; }
for(int i=1;i<=n;++i)
if(t[i]==1){
t2[i]=1;
for(int j=e1[i].size()-1;j>=0;--j)
if(t[e1[i][j]]==0)
{t2[i]=0;break;}
}//判断该点的所有出边是否都能到终点
if(t2[fir]==0)
{ cout<<"-1"<<endl;return 0; }
q.push(fir);f[fir]=1;
while(!q.empty()){//从最终选出的点中找最短路径
int z=q.front();q.pop();
if(z==end){
cout<<f[z]-1<<endl;
return 0;//由广搜性质可知最先搜到路径一定最短
}
for(int i=e1[z].size()-1;i>=0;--i){
int next=e1[z][i];
if(t2[next]==1&&f[next]==0){//注意要有f[next]==0的特判,否则会超时
q.push(next);
f[next]=f[z]+1;
}
}
}cout<<"-1"<<endl;
return 0;
}
P3243 [HNOI2015] 菜肴制作
先说结论:建反图,然后在反图上用大根堆拓扑排序。
证明来自讨论区:
题目要求1、2、3依次尽量靠前
我们发现这种反向方法最后一个数一定是可能的最大值,倒数第二个数一定是在此条件下除去最后一个的最大值,以此类推
所以在这种方法中,我们会尽量早使用尽可能大的序号,尽量晚使用1、2、3等较小序号
也即1尽量靠前、在此标准下2尽量靠前... ...
void f(){
for(int i=1;i<=n;++i)
if(in[i]==0)
q.push(i);
while(!q.empty()){
int s=q.top();q.pop();
ans[++tot]=s;
for(int i=0;i<p[s].size();++i){
--in[p[s][i]];
if(in[p[s][i]]==0)
q.push(p[s][i]);
}
}
}
void _clear(){
for(int i=1;i<=n;++i)
p[i].clear(),in[i]=0;
tot=0;
}
int main(){
cin>>t;
while(t --> 0){
_clear();
cin>>n>>m;
for(int i=1;i<=m;++i){
cin>>x>>y;
p[y].push_back(x);
++in[x];
}
f();
if(tot<n)
cout<<"Impossible!";
else
for(int i=tot;i>=1;--i)
cout<<ans[i]<<" ";
cout<<endl;
}
return 0;
}
P1613 跑路
因为每次可以跑
int main(){
scanf("%lld%lld",&n,&m);
ans=1e9;
for(int i=1;i<=m;++i){
scanf("%lld%lld",&u,&v);
e[u][v][0]=1;
}
for(int l=1;l<=31;++l)
for(int k=1;k<=n;++k)
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
e[i][j][l]=e[i][j][l]|(e[i][k][l-1]&e[k][j][l-1]);
for(int l=0;l<=31;++l)
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
if(i!=j){
if(e[i][j][l]) g[i][j]=1;
else if(g[i][j]==0)
g[i][j]=1e9;
}
for(int k=1;k<=n;++k)
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
g[i][j]=min(g[i][j],g[i][k]+g[k][j]);
printf("%lld\n",g[1][n]);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】