最短路,最小生成树,及拓扑排序都是图论基础算法,这里不再赘述,只进行模板整理,如有疑问还请评论留言
对于最短路算法,这里除常用的SPFA和堆优化Dijkstra外,还整理了许多学校和算法竞赛培训机构不会教授的Bellman_ford与原版Dijkstra,以便于读者理解前面的两种优化版本。
最短路:
最短路算法经典例题
http://codevs.cn/problem/1557/热浪
//Bellman_ford版本
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
const int INF=100000000;
using namespace std;
int t,c,ts,te,cnt;
int u[100000],v[100000],d[100000],w[100000];
void Bellman_ford()
{
for(int i=1;i<=t;i++)
d[i]=INF;
d[ts]=0;
for(int i=1;i<=t-1;i++)
for(int j=1;j<=cnt;j++)
{
if(d[u[j]]<INF)
d[v[j]]=min(d[v[j]],d[u[j]]+w[j]);
}
}
int main()
{
scanf("%d%d%d%d",&t,&c,&ts,&te);
cnt=c;
for(int i=1;i<=c;i++)
{
scanf("%d%d%d",&u[i],&v[i],&w[i]);
u[++cnt]=v[i];
v[cnt]=u[i];
w[cnt]=w[i];
}
Bellman_ford();
printf("%d",d[te]);
return 0;
}
//SPFA邻接链表版本
//SPFA是Bellman_ford的队列优化
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
using namespace std;
const int INF=1000000000;
int t,c,ts,te,ru,rv,rw,tot,x;
int d[100000],first[100000],next[100000];
bool inq[100000];
queue<int>q;
struct Edge
{
int u,v,w;
}l[100000];
void build(int u,int v,int w)
{
l[++tot]=(Edge){u,v,w};
next[tot]=first[u];
first[u]=tot;
}
void SPFA()
{
for(int i=1;i<=t;i++)
d[i]=INF;
d[ts]=0;
inq[ts]=1;
q.push(ts);
while(!q.empty())
{
int k=q.front();
q.pop();
inq[k]=0;
for(int i=first[k];i!=-1;i=next[i])
{
x=l[i].v;
if(d[k]<INF&&d[x]>d[k]+l[i].w)
{
d[x]=d[k]+l[i].w;
if(!inq[x])
q.push(x);
inq[x]=1;
}
}
}
}
int main()
{
memset(first,-1,sizeof(first));
scanf("%d%d%d%d",&t,&c,&ts,&te);
for(int i=1;i<=c;i++)
{
scanf("%d%d%d",&ru,&rv,&rw);
build(ru,rv,rw);
build(rv,ru,rw);
}
SPFA();
printf("%d",d[te]);
return 0;
}
//SPFA邻接表版本
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
#include<vector>
using namespace std;
const int INF=1000000000;
int t,c,ts,te,ru,rv,rw,tot,x,w;
int d[100000];
bool inq[100000];
vector<int>G[100000];
vector<int>V[100000];
queue<int>q;
inline void build(int u,int v,int w)
{
G[u].push_back(v);
V[u].push_back(w);
G[v].push_back(u);
V[v].push_back(w);
}
void SPFA()
{
for(int i=1;i<=t;i++)
d[i]=INF;
d[ts]=0;
inq[ts]=1;
q.push(ts);
while(!q.empty())
{
int k=q.front();
q.pop();
inq[k]=0;
for(int i=0;i<G[k].size();i++)
{
x=G[k][i];
w=V[k][i];
if(d[k]<INF&&d[x]>d[k]+w)
{
d[x]=d[k]+w;
if(!inq[x])
q.push(x);
inq[x]=1;
}
}
}
}
int main()
{
scanf("%d%d%d%d",&t,&c,&ts,&te);
for(int i=1;i<=c;i++)
{
scanf("%d%d%d",&ru,&rv,&rw);
build(ru,rv,rw);
}
SPFA();
printf("%d",d[te]);
return 0;
}
//Dijkstra版本
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
const int INF=1000000000;
int t,c,ts,te,x,y,r;
int d[1000001];
int w[2500][2500];
bool v[1000001];
void dijkstra()
{
for(int i=1;i<=t;i++)
d[i]=INF;
d[ts]=0;
for(int i=1;i<=t;i++)
{
int k=0,m=INF;
for(int j=1;j<=t;j++)
{
if(!v[j]&&d[j]<=m)
{
m=d[j];
k=j;
}
}
v[k]=1;
for(int j=1;j<=t;j++)
if(!v[j])
d[j]=min(d[j],d[k]+w[k][j]);
}
}
int main()
{
scanf("%d%d%d%d",&t,&c,&ts,&te);
for(int i=1;i<=t;i++)
for(int j=1;j<=t;j++)
{
if(i!=j)
w[i][j]=INF;
}
for(int i=1;i<=c;i++)
{
scanf("%d%d%d",&x,&y,&r);
w[x][y]=w[y][x]=r;
}
dijkstra();
printf("%d",d[te]);
return 0;
}
//Dijkstra,堆优化,邻接链表版本
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int MAXN = 100000 + 5;
int t,c,ts,te,tot,rs,re,rc;
int dis[MAXN],first[MAXN],next[MAXN];
bool done[MAXN];
struct edge
{
int f,t,v;
}l[MAXN << 1];
struct zt
{
int num;
int d;//距离用于排序
/*bool operator < (zt a)const
{
return d>a.d;
}
friend bool operator < (zt a,zt b)
{
return a.d<b.d;
}*///2种写法,仅对结构体里的小于号生效
}p[MAXN];
priority_queue<zt>q;
bool operator < (zt a,zt b)
{
return a.d>b.d;
}
void build(int f,int t,int v)
{
l[++tot]=(edge){f,t,v};
next[tot]=first[f];
first[f]=tot;
}
void dijkstra()
{
while(!q.empty()) q.pop();
q.push((zt){ts,0});
dis[ts]=0;
while(!q.empty())
{
zt a=q.top();
int u=a.num;
q.pop();
if(done[u]==1)
continue;
done[u]=1;
for(int i=first[u];i!=-1;i=next[i])
{
int v=l[i].t;
if(dis[v]>dis[u]+l[i].v)
{
dis[v]=dis[u]+l[i].v;
q.push((zt){v,dis[v]});
}
}
}
}
int main()
{
memset(dis,0x3f,sizeof(dis));
memset(first,-1,sizeof(first));
scanf("%d%d%d%d",&t,&c,&ts,&te);
for(int i=1;i<=c;i++)
{
scanf("%d%d%d",&rs,&re,&rc);
build(rs,re,rc);
build(re,rs,rc);
}
dijkstra();
printf("%d",dis[te]);
return 0;
}
http://codevs.cn/problem/1021/玛丽卡
注意题目描述中“最糟糕的情况”指的是玛丽卡。
对答案有贡献的路径只存在于最短路上
先跑一边SPFA找出最短路上的边,依次尝试删除每条边,跑SPFA统计答案即可
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
using namespace std;
const int INF = 1000000007;
int n,m,ru,rv,rw,tot,ans;
int dis[500010],first[1000010],nxt[1000010],pre[500010];
bool flag;
bool inq[100010];
struct edge
{
int u,v,w;
}l[1000010];
queue<int>q;
void build(int f,int t,int c)
{
l[++tot]=(edge){f,t,c};
nxt[tot]=first[f];
first[f]=tot;
}
void SPFA(int l1,int l2)
{
for(int i=1;i<=n;i++)
dis[i]=INF;
dis[1]=0;
q.push(1);
inq[1]=1;
while(!q.empty())
{
int k=q.front();
q.pop();
inq[k]=0;
for(int i=first[k];i!=-1;i=nxt[i])
{
int x=l[i].v;
if((k==l1&&x==l2)||(k==l2&&x==l1))
continue;
if(dis[x]>dis[k]+l[i].w)
{
dis[x]=dis[k]+l[i].w;
if(!flag)
pre[x]=k;//记录最短路径上的边
if(!inq[x])//若每次更新都记录起点及起点出发的边的编号会导致记录部分不在最短路径上的边...虽然不影响答案
{
q.push(x);
inq[x]=1;
}
}
}
}
}
int main()
{
memset(first,-1,sizeof(first));
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&ru,&rv,&rw);
build(ru,rv,rw);
build(rv,ru,rw);
}
tot=0;
SPFA(0,0);
flag=1;
for(int i=n;i;i=pre[i])
{
SPFA(i,pre[i]);
ans=max(ans,dis[n]);
}
printf("%d",ans);
return 0;
}
最小生成树:
http://codevs.cn/problem/1231/最优布线问题
//Kruskal版本
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
typedef unsigned long long LL;
int n,m,cnt,x,y,e;
int u[1000000],v[1000000],w[1000000],r[1000000],fa[1000000];
LL cost;
bool cmp(int a,int b)
{
return w[a]<w[b];
}
int find(int x)
{
return fa[x]==x?x:fa[x]=find(fa[x]);
}
void kruskal()
{
for(int i=1;i<=n;i++)
fa[i]=i;
for(int i=1;i<=m;i++)
r[i]=i;
sort(r+1,r+m+1,cmp);
for(int i=1;i<=m;i++)
{
e=r[i];
x=find(u[e]);
y=find(v[e]);
if(x!=y)
{
cost+=w[e];
fa[x]=y;
}
}
}
int main()
{
scanf("%d%d",&n,&m);
cnt=n;
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&u[i],&v[i],&w[i]);
}
kruskal();
printf("%lld",cost);
return 0;
}
//Prim
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
using namespace std;
typedef unsigned long long LL;
const int MAXN=1000000;
LL n,m,rf,rt,rv,tot,cost,cnt;
LL first[MAXN],next[MAXN];
bool done[MAXN];
struct Edge
{
LL f,t,v;
}l[MAXN];
struct condition
{
LL num,d;
}p[MAXN];
priority_queue<condition>q;
bool operator < (condition a,condition b)
{
return a.d>b.d;
}
void build(int f,int t,int v)
{
l[++tot]=(Edge){f,t,v};
next[tot]=first[f];
first[f]=tot;
}
void prim()
{
while(!q.empty()) q.pop();
q.push((condition){1,0});
while(!q.empty()&&cnt<n)
{
condition a=q.top();
LL u=a.num;
LL w=a.d;
q.pop();
if(done[u])
continue;
done[u]=1;
cost+=w;
cnt++;
for(int i=first[u];i!=-1;i=next[i])
if(!done[l[i].t])
q.push((condition){l[i].t,l[i].v});
}
}
int main()
{
memset(first,-1,sizeof(first));
scanf("%lld%lld",&n,&m);
for(int i=1;i<=m;i++)
{
scanf("%lld%lld%lld",&rf,&rt,&rv);
build(rf,rt,rv);
build(rt,rf,rv);
}
prim();
printf("%lld",cost);
return 0;
}
拓扑排序:
http://codevs.cn/problem/2833/奇怪的梦境
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
using namespace std;
queue<int>q;
int n,m,ru,rv,tot,cnt,fla;
int first[100001],next[100001],pas[100001],ans[100001],jud1[100001],jud2[100001];
bool flag;
struct spot
{
int u,v;
}l[100001];
void build(int f,int t)
{
l[++tot]=(spot){f,t};
next[tot]=first[f];
first[f]=tot;
}
int main()
{
memset(first,-1,sizeof(first));
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&ru,&rv);
if(jud1[ru]==rv)
continue;
pas[rv]++;
jud1[ru]=rv;
build(ru,rv);
}
for(int i=1;i<=n;i++)
if(pas[i]==0)
q.push(i);
while(!q.empty())
{
int x=q.front();
q.pop();
ans[++cnt]=x;
for(int i=first[x];i!=-1;i=next[i])
{
pas[l[i].v]--;
if(pas[l[i].v]==0)
q.push(l[i].v);
}
}
if(cnt>=n)
printf("o(∩_∩)o");
else
{
printf("T_T\n");
printf("%d",n-cnt);
}
return 0;
}