P3916 图的遍历
反向建边 + dfs
按题目来每次考虑每个点可以到达点编号最大的点,不如考虑较大的点可以反向到达哪些点
循环从N到1,则每个点i能访问到的结点的A值都是i
每个点访问一次,这个A值就是最优的,因为之后如果再访问到这个结点那么答案肯定没当前大了
#include<iostream>
#include<cmath>
#include<string>
#include<algorithm>
#include<iomanip>
#include<vector>
const int N=100010;
using namespace std;
vector<int>G[N];//vector存图
int n,m;
int A[N];
void dfs(int x,int d){
if(A[x]!=0)return ;//访问过
A[x]=d;
for(int i=0;i<G[x].size();i++){
dfs(G[x][i],d);
}
}
int main(){
int x,y;
cin>>n>>m;
while(m--){
cin>>x>>y;
G[y].push_back(x);//反向建边
}
for(int i=n;i;i--){
dfs(i,i);
}
for(int i=1;i<=n;i++){
cout<<A[i]<<" ";
}
return 0;
}
P1807 最长路
#include<iostream>
#include<cmath>
#include<string>
#include<algorithm>
#include<iomanip>
#include<vector>
using namespace std;
int dis[50001],w[50001];
int n,m,minn,f[50001][3];
int main(){
cin>>n>>m;
for(int i=1;i<=m;i++){
dis[i]=w[i]=100000;
f[i][1]=f[i][2]=0;
}
for(int i=1;i<=m;i++){
int a,b,c;
cin>>a>>b>>c;
f[i][1]=a,f[i][2]=b,w[i]=-c;
}
dis[1]=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
dis[f[j][2]]=min(dis[f[j][2]],dis[f[j][1]]+w[j]);
}
}
if(dis[n]!=0){
cout<<-dis[n];
}else{
cout<<"-1";
}
return 0;
}
P2853 [USACO06DEC]Cow Picnic S
从k个奶牛分别dfs,用mk[i]表示第i个牧场被遍历过多少次,最后只有mk[i]==k的牧场满足条件。无权的有向图也可以用vector存储。
#include<iostream>
#include<cmath>
#include<string>
#include<algorithm>
#include<iomanip>
#include<vector>
using namespace std;
int k,N,M;
//int G[10001][10010];
vector<int>G[10001];
bool vis[10001];
int a[10001],ans[10001];
int cnt;
void dfs(int x){
if(!vis[x])ans[x]++;
else vis[x]=1;
for(int i=0;i<G[x].size();i++){
dfs(G[x][i]);
}
}
int main(){
cin>>k>>N>>M;
for(int i=1;i<=k;i++)cin>>a[i];
for(int i=1;i<=M;i++){
int x,y;
cin>>x>>y;
G[x].push_back(y);
}
for(int i=1;i<=k;i++){
for(int j=1;j<=N;j++){
vis[j]=0;
}
dfs(a[i]);
}
for(int i=1;i<=N;i++){
if(ans[i]==k)cnt++;
}
cout<<cnt;
return 0;
}
P4017 最大食物链计数
1、这是一道图论题;
2、不是求最短路;
3、根据提示“最左端是不会捕食其他生物的生产者”可以想到,我们要入度为零的点开始查找;
4、再看一遍题目,就是求路径数,当且仅当一个点的入度变为零时才需要入队,并不是数据更新一次就要入队;
5、出度为零的点的路径总数和就是答案。
思路已经呼之欲出了:拓扑排序!```
```cpp
在这里插入代码片
拓扑排序模板:
#include<iostream>
#include<cmath>
#include<string>
#include<algorithm>
#include<iomanip>
#include<vector>
#include<queue>
using namespace std;
queue<int>q;
int mp[5005][5005];
int n,m,ru[5005],chu[5005],a,b,f[5005],ans;
int main(){
cin>>n>>m;
for(int i=1;i<=m;i++){
cin>>a>>b;
mp[a][b]=1;
chu[a]++;
ru[b]++;
}
//步骤1
for(int i=1;i<=n;i++){
if(ru[i]==0){
f[i]=1;
q.push(i);
}
}
while(!q.empty())
{
int a=q.front();
q.pop();
//步骤二
for(int k=1;k<=n;k++)
{
/*
一些操作
*/
if(ru[k]==0){//步骤三
/*if(chu[k]==0){
ans+=f[k];
ans%=80112002;
continue;
}*/
//其他操作 }
q.push(k);
}
}
}
cout<<ans;
return 0;
}
对于这道题:
#include<iostream>
#include<cmath>
#include<string>
#include<algorithm>
#include<iomanip>
#include<vector>
#include<queue>
using namespace std;
queue<int>q;
int mp[5005][5005];
int n,m,ru[5005],chu[5005],a,b,f[5005],ans;
int main(){
cin>>n>>m;
for(int i=1;i<=m;i++){
scanf("%d%d", &a, &b);
mp[a][b]=1;
chu[a]++;
ru[b]++;
}
for(int i=1;i<=n;i++){
if(ru[i]==0){
f[i]=1;
q.push(i);
}
}
while(!q.empty())
{
int a=q.front();
q.pop();
for(int k=1;k<=n;k++)
{
if(mp[a][k]==0)continue;
f[k]+=f[a];
f[k]%=80112002;
ru[k]--;
if(ru[k]==0){
if(chu[k]==0){
ans+=f[k];
ans%=80112002;
continue;
}
q.push(k);
}
}
}
cout<<ans;
return 0;
}
P1113 杂务
拓扑排序
总结一下,此种拓扑排序共有四个主要步骤:
- 初始化队列,将入度为 00 的节点放入队列。
- 取出队首,遍历其出边,将能够到达的点入度减一,同时维护答案数组。
- 若在此时一个点的入度变为 11,那么将其加入队列。
- 回到第二步,直到队列为空。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cctype>
#include <vector>
#include <queue>
#define ll long long
using namespace std;
inline int read() {
int x=0,f=1;
char ch=getchar();
while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();}
while (isdigit(ch)) x=x*10+ch-'0',ch=getchar();
return x*f;
}
const int N=500005;
int ind[N],f[N],a[N]; //ind--入度 f--答案 a--时间
vector <int> edge[N];
queue <int> q;
int main() {
int n=read();
for (int i=1;i<=n;i++) {
int x=read();
a[i]=read();
while (int y=read()) {
if (!y) break;
edge[y].push_back(x);
ind[x]++;
}
}
//步骤一
for (int i=1;i<=n;i++) {
if (ind[i]==0) {
q.push(i);
f[i]=a[i];
}
};
while (!q.empty()) {
int rhs=q.front();
q.pop();
//步骤二
for (int i=0;i<edge[rhs].size();i++) {
int u=edge[rhs][i];
ind[u]--;
if (ind[u]==0) q.push(u); //步骤三
f[u]=max(f[u],f[rhs]+a[u]);
}
}
int ans=0;
for (int i=1;i<=n;i++) {
ans=max(ans,f[i]); //统计答案
}
printf("%d\n",ans);
return 0;
}