图论基础,最短路,最小生成树专题
一:拓扑排序
1.神经网路
原题链接:https://ac.nowcoder.com/acm/contest/87814/1001
拓扑板子
查看代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
int head[2000000],res,n,m,c[1000000],v[1000000],ru[1000000],chu[1000000];
struct E{
int t,l,next;
}edge[2000000];
void lin(int x,int y,int z)//链式向前星
{
edge[++res].t=y;
edge[res].l=z;
edge[res].next=head[x];
head[x]=res;
}
queue<int>q;
signed main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>c[i]>>v[i];
if(c[i]>0)q.push(i);
}
for(int i=1;i<=m;i++)
{
int x,y,z;
cin>>x>>y>>z;
lin(x,y,z);
ru[y]++,chu[x]++;
}
while(!q.empty())
{
int x=q.front();
q.pop();
for(int i=head[x];i;i=edge[i].next)
{
ru[edge[i].t]--;
if(c[x]>0)c[edge[i].t]+=edge[i].l*c[x];
if(ru[edge[i].t]==0)
{
q.push(edge[i].t);
c[edge[i].t]-=v[edge[i].t];
}
}
}
int t=0;
for(int i=1;i<=n;i++)
{
if(chu[i]==0 and c[i]>0)cout<<i<<" "<<c[i]<<endl,t=1;
}
if(!t)cout<<"NULL";
return 0;
}
2.菜肴制作
原题链接:https://ac.nowcoder.com/acm/contest/87814/1003
正向建图考虑的因素过多,所以反向建图再利用拓扑即可
查看代码
#include <bits/stdc++.h>//拓扑排序
#define int long long
using namespace std;
int head[2000000],res,n,m,c[1000000],v[1000000],ru[1000000],chu[1000000],da[1000000];
struct E{
int t,next;
}edge[2000000];
void lin(int x,int y)//链式向前星
{
edge[++res].t=y;
edge[res].next=head[x];
head[x]=res;
}
priority_queue<int>q;
bool topu()
{
while(!q.empty())q.pop();
for(int i=1;i<=n;i++)
{
if(ru[i]==0)q.push(i);
}
int num=0;
while(!q.empty())
{
int x=da[++num]=q.top();
q.pop();
for(int i=head[x];i!=-1;i=edge[i].next)
{
ru[edge[i].t]--;
if(ru[edge[i].t]==0)
{
q.push(edge[i].t);
}
}
}
if(num<n)return false;
else return true;
}
signed main()//反向建图
{
int T;
cin>>T;
while(T--)
{
memset(head,-1,sizeof head);
memset(ru,0,sizeof ru);
cin>>n>>m;
for(int i=1;i<=m;i++)
{
int x,y;
cin>>x>>y;
lin(y,x);
ru[x]++;
}
if(!topu())cout<<"Impossible!"<<endl;
else
{
for(int i=n;i>=1;i--)
{
cout<<da[i]<<" ";
}
cout<<endl;
}
}
return 0;
}
二:最短路
1.公交线路
原题链接:https://ac.nowcoder.com/acm/contest/87814/1002
dijkstra做法
查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=6e6+10;
bool sign[N];
int h[N],e[N],ne[N],w[N],idx,n,m,s,t;
int dis[N];
typedef pair<int,int> PII;
void add(int x,int y,int z)
{
e[idx]=y;
w[idx]=z;
ne[idx]=h[x];
h[x]=idx++;
}
int djs(int a,int b)
{
memset(dis,0x3f,sizeof dis);
dis[a]=0;
priority_queue<PII,vector<PII>,greater<PII>> p;
p.push({0,a});
while(p.size())
{
PII t=p.top();
p.pop();
int dian=t.second,distance=t.first;
if(sign[dian])continue;
sign[dian]=true;
for(int i=h[dian];i!=-1;i=ne[i])
{
int j=e[i];
if(dis[j]>distance+w[i])
{
dis[j]=distance+w[i];
p.push({dis[j],j});
}
}
}
if(dis[b]==0x3f3f3f3f)return -1;
return dis[b];
}
int main()
{
memset(h,-1,sizeof h);
cin>>n>>m>>s>>t;
for(int j=1;j<=m;j++)
{
int x,y,z;
cin>>x>>y>>z;
add(x,y,z);
add(y,x,z);
}
cout<<djs(s,t);
return 0;
}
spfa做法
查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=2e6+10;
bool sign[N];
int h[N],e[N],ne[N],w[N],vis[N],idx,n,m,s,t;
int dis[N];
void add(int x,int y,int z)
{
e[idx]=y;
w[idx]=z;
ne[idx]=h[x];
h[x]=idx++;
}
queue<int>q;
int spfa(int s,int t)
{
memset(dis,0x3f,sizeof dis);
memset(vis,0,sizeof vis);
dis[s]=0;
vis[s]=1;
q.push(s);
while(!q.empty())
{
int x=q.front();
q.pop();
vis[x]=0;
for(int i=h[x];i!=-1;i=ne[i])
{
int y=e[i];
if(dis[y]>dis[x]+w[i])
{
dis[y]=dis[x]+w[i];
if(!vis[y])
{
q.push(y);
vis[y]=1;
}
}
}
}
if(dis[t]>=0x3f3f3f3f)return -1;
else return dis[t];
}
int main()
{
memset(h,-1,sizeof h);
cin>>n>>m>>s>>t;
for(int j=1;j<=m;j++)
{
int x,y,z;
cin>>x>>y>>z;
add(x,y,z);
add(y,x,z);
}
cout<<spfa(s,t);
return 0;
}
2.Meeting
原题链接:https://ac.nowcoder.com/acm/contest/87814/1022
重点是建图,按正常思维建图会t,那么我们就虚拟出一个点与已知点建边,最后将距离除二即可
查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e6;
bool sign[N];
int h[N],e[N],ne[N],w[N],idx,n,m;
int dis[N],dist[N];
typedef pair<int,int> PII;
int a[1000000];
void add(int x,int y,int z)
{
e[idx]=y;
w[idx]=z;
ne[idx]=h[x];
h[x]=idx++;
}
void djs(int x,int d[])
{
for(int i=0;i<=n+m;i++)d[i]=LLONG_MAX;
memset(sign,0,sizeof sign);
d[x]=0;
priority_queue<PII,vector<PII>,greater<PII>> p;
p.push({0,x});
while(p.size())
{
PII t=p.top();
p.pop();
int dian=t.second,distance=t.first;
if(sign[dian])continue;
sign[dian]=true;
for(int i=h[dian];i!=-1;i=ne[i])
{
int j=e[i];
if(d[j]>distance+w[i])
{
d[j]=distance+w[i];
p.push({d[j],j});
}
}
}
}
signed main()
{
int t;
cin>>t;
int ans=1;
while(t--)
{
memset(h,-1,sizeof h);
cin>>n>>m;
for(int i=1;i<=m;i++)
{
int time,num;
cin>>time>>num;
while(num--)
{
int x;
cin>>x;
add(x,n+i,time);//不可以团内两两加边,那就每个团虚拟出来一个点,给定的边权就变成了团内点都与这个点相连,只不过时间会翻倍
add(n+i,x,time);//n+i就是虚化出的点
}
}
cout<<"Case #"<<ans++<<": ";
djs(1,dis);
djs(n,dist);
int mi=1e18;
for(int i=1;i<=n;i++)
{
mi=min(mi,max(dis[i],dist[i]));
}
if(mi==1e18)cout<<"Evil John"<<endl;
else
{
cout<<mi/2<<endl;
for(int i=1;i<=n;i++)
{
if(max(dis[i],dist[i])==mi)cout<<i<<" ";
}
cout<<endl;
}
}
return 0;
}
三:最小生成树
1.挖沟
原题链接:https://ac.nowcoder.com/acm/contest/87814/1007
prim做法
查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
struct lian{
int t,l,next;
}edge[2000000];
struct node{
int x,len;
bool operator<(const node &a) const
{
return len>a.len;
}
};
int res=0,head[2000000];
int sign[2000000];
void add(int x,int y,int z)
{
edge[res].t=y;
edge[res].l=z;
edge[res].next=head[x];
head[x]=res++;
}
void prim()
{
priority_queue<node>q;
sign[1]=1;
node tmp;
for(int i=head[1];i!=-1;i=edge[i].next)
{
tmp.x=edge[i].t;
tmp.len=edge[i].l;
q.push(tmp);
}
int sum=0;
while(!q.empty())
{
node tt=q.top();
q.pop();
int x=tt.x;
if(sign[tt.x])continue;
sign[tt.x]=1;
sum+=tt.len;
for(int i=head[x];i!=-1;i=edge[i].next)
{
if(sign[edge[i].t])continue;
tt.x=edge[i].t;
tt.len=edge[i].l;
q.push(tt);
}
}
cout<<sum;
}
signed main()
{
memset(head,-1,sizeof head);
int n,m;
cin>>n>>m;
for(int i=1;i<=m;i++)
{
int a,b,v;
cin>>a>>b>>v;
add(a,b,v);
add(b,a,v);
}
prim();
}
kruskal做法
查看代码
#include<bits/stdc++.h>//Kruskal算法
#define int long long
using namespace std;
int fa[2000000];
struct node
{
int x,y,z;
}edge[2000000];
bool cmp(node a,node b)
{
return a.z<b.z;
}
int find(int x)
{
return fa[x]==x?x:fa[x]=find(fa[x]);
}
signed main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)fa[i]=i;
for(int i=1;i<=m;i++)
{
cin>>edge[i].x>>edge[i].y>>edge[i].z;
}
sort(edge+1,edge+m+1,cmp);
int ans=0;
for(int i=1;i<=m;i++)
{
int fx=find(edge[i].x);
int fy=find(edge[i].y);
if(fx==fy)continue;
ans+=edge[i].z;
fa[fx]=fy;
}
cout<<ans;
return 0;
}
补充从某点到某点能经过的最大的最小距离值
查看代码
#include<bits/stdc++.h>//Kruskal算法
#define int long long
using namespace std;
int fa[2000000];
struct node
{
int x,y,z;
}edge[2000000];
bool cmp(node a,node b)
{
return a.z<b.z;
}
int find(int x)
{
return fa[x]==x?x:fa[x]=find(fa[x]);
}
signed main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)fa[i]=i;
for(int i=1;i<=m;i++)
{
cin>>edge[i].x>>edge[i].y>>edge[i].z;
}
int t1,t2;
cin>>t1>>t2;
sort(edge+1,edge+m+1,cmp);
int ans=0;
for(int i=1;i<=m;i++)
{
int fx=find(edge[i].x);
int fy=find(edge[i].y);
if(fx==fy)continue;
fa[fx]=fy;
if(find(t1)==find(t2))
{
cout<<edge[i].z;
return 0;
}
ans+=edge[i].z;
}
return 0;
}
2.Forsanken喜欢独一无二的树
原题链接https://ac.nowcoder.com/acm/contest/87814/1009
正常的kruskal做法,利用双指针去重边
查看代码
#include<bits/stdc++.h>//Kruskal算法
#define int long long
using namespace std;
int fa[2000000];
struct node
{
int x,y,z;
}edge[2000000];
bool cmp(node a,node b)
{
return a.z<b.z;
}
int find(int x)
{
return fa[x]==x?x:fa[x]=find(fa[x]);
}
signed main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)fa[i]=i;
for(int i=1;i<=m;i++)
{
cin>>edge[i].x>>edge[i].y>>edge[i].z;
}
sort(edge+1,edge+m+1,cmp);
int ans=0;
for(int i=1;i<=m;)
{
int l=i,r=i;
while(edge[r].z==edge[l].z)r++;
for(int j=l;j<r;j++)
{
int f1=find(edge[j].x),f2=find(edge[j].y);
if(f1!=f2)ans+=edge[j].z;
}
for(int j=l;j<r;j++)
{
int f1=find(edge[j].x),f2=find(edge[j].y);
if(f1!=f2)fa[f1]=f2,ans-=edge[j].z;
}
i=r;
}
cout<<ans;
return 0;
}