基础知识
两种存储方式:邻接矩阵、邻接表。
两种遍历:dfs,bfs。
图的遍历
考虑深搜,发现出现环的情况进行不下去。
一种可行的方案是反向建边后从最后一个点往后开始 dfs 或 bfs。
#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
const int maxn = 1e5+3;
vector<int> p[maxn];
int a[maxn];
void dfs(int x,int v)
{
a[x]=v;
for(int i=0;i<p[x].size();i++)
{
if(!a[p[x][i]])
dfs(p[x][i],v);
}
}
void solve()
{
int n,m; cin>>n>>m;
int u,v;
for(int i=1;i<=m;i++) // 反向建边
{
cin>>u>>v;
p[v].push_back(u);
}
for(int i=n;i>=1;i--)
{
if(!a[i])
{
dfs(i,i);
}
}
for(int i=1;i<=n;i++)
cout<<a[i]<<" ";
return ;
}
int main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int T; T=1;
while(T--) solve();
return 0;
}
DAG 与 拓扑排序
DAG:有向无环图。
拓扑排序:每次选入度为 0 的点,删除它和它的出边。
拓扑排序的结果不唯一,例如在上图中,a-e-b-c-d 或 a-b-e-c-d 或 a-b-c-e-d 都是正确的答案。
拓扑排序只能在 DAG 上进行,有向才能确定删除的顺序,无环才能保证排序过程正确进行,如果有环的话会导致排序到某一步出现没有入度为 0 的点。
同理,如果拓扑排序进行不下去了,说明这个图里有环。
最大食物链计数
求食物链的总数,必须从生产者开始,到最高级消费者结束,模拟拓扑排序的过程即可。
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int mod = 80112002;
const int maxn = 5e3+3;
int outd[maxn],ind[maxn];
int f[maxn]; // 记录以 i 为终点的方案数
vector<int>p[maxn];
queue<int>q;
void solve()
{
int n,m;
cin>>n>>m;
int u,v;
for(int i=1;i<=m;i++)
{
cin>>u>>v;
p[u].push_back(v);
outd[u]++;
ind[v]++;
}
for(int i=1;i<=n;i++)
{
if(!ind[i]) // 将入度为 0 的点加入队列
{
q.push(i);
f[i]=1; // 初始时,以入度为 0 的点作为子食物链的数量为 1
}
}
while(!q.empty())
{
int x=q.front();
q.pop();
for(int i=0;i<p[x].size();i++)
{
int y=p[x][i];
f[y]=(f[x]+f[y])%mod;
ind[y]--;
if(!ind[y]) q.push(y);
}
}
int ans=0;
for(int i=1;i<=n;i++)
{
if(!outd[i])
ans=(ans+f[i])%mod;
}
cout<<ans<<endl;
return ;
}
signed main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int T; T=1;
while(T--) solve();
return 0;
}
最长路
除了 1 之外可能还有入度为 0 的点,因此要在跑拓扑排序前先把这些点对结果的影响消除。注意到边权可能为负数,因此初始化存最长路的数组时注意不要初始化为 0。
#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
const int maxn = 1e5+3;
struct edge
{
int to,len;
};
vector<edge>p[maxn]; // 存边和边权
int f[maxn],ind[maxn]; // 存最长路和入度
queue<int>q;
void solve()
{
int n,m;
cin>>n>>m;
int u,v,w;
for(int i=1;i<=m;i++)
{
cin>>u>>v>>w;
p[u].push_back({v,w});
ind[v]++;
}
for(int i=2;i<=n;i++)
{
f[i]=-1e9;
if(!ind[i]) q.push(i);
}
// 将除 1 外入度为 0 的点的影响消除
while(!q.empty())
{
int x=q.front();
q.pop();
for(int i=0;i<p[x].size();i++)
{
ind[p[x][i].to]--;
if(!ind[p[x][i].to]) q.push(p[x][i].to);
}
}
q.push(1);
while(!q.empty())
{
int x=q.front();
q.pop();
for(int i=0;i<p[x].size();i++)
{
f[p[x][i].to]=max(f[p[x][i].to],f[x]+p[x][i].len);
ind[p[x][i].to]--;
if(!ind[p[x][i].to]) q.push(p[x][i].to);
}
}
if(f[n]==-1e9) cout<<-1<<endl;
else cout<<f[n]<<endl;
return ;
}
int main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int T; T=1;
while(T--) solve();
return 0;
}
车站分级
由于每次停靠都是按顺序来的,因此每段停靠区间里没有出现过的车站一定比出现过的等级要低,依据此进行建图,用等级低的车站指向等级高的车站,然后拓扑排序每次删去所有入度为0的点,最后看拓扑排序由多少层即可。
#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
const int maxn = 1e3+5;
bool pd[maxn]; // 记录每组数据的车站停靠情况
int a[maxn];
int p[maxn][maxn];
int in[maxn]; // 记录每个节点的入度
int ans;
void solve()
{
int n,m;
cin>>n>>m;
while(m--)
{
memset(pd,0,sizeof(pd));
int s; cin>>s;
for(int i=1;i<=s;i++)
{
cin>>a[i];
pd[a[i]]=true;
}
for(int i=a[1];i<=a[s];i++)
{
if(!pd[i])
{
for(int j=1;j<=s;j++)
{
if(!p[i][a[j]])
{
p[i][a[j]]=1;
in[a[j]]++;
}
}
}
}
}
queue<int> q;
// 将入度为 0 的点加入队列
for(int i=1;i<=n;i++)
{
if(!in[i]) q.push(i);
}
// 拓扑排序 一次处理一层
while(!q.empty())
{
int size=q.size();
ans++;
for(int i=0;i<size;i++)
{
int x=q.front();
q.pop();
for(int j=1;j<=n;j++)
{
if(p[x][j])
{
in[j]--;
p[x][j]=0;
if(!in[j]) q.push(j);
}
}
}
}
cout<<ans<<endl;
return ;
}
int main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int T; T=1;
while(T--) solve();
return 0;
}
标签:图论,int,--,cin,push,算法,maxn,ind,-----
From: https://blog.csdn.net/jinxdtaiy/article/details/141200011