P5318 【深基18.例3】查找文献
题目描述
小 K 喜欢翻看洛谷博客获取知识。每篇文章可能会有若干个(也有可能没有)参考文献的链接指向别的博客文章。小 K 求知欲旺盛,如果他看了某篇文章,那么他一定会去看这篇文章的参考文献(如果他之前已经看过这篇参考文献的话就不用再看它了)。
假设洛谷博客里面一共有 n(1≤n≤105) 篇文章(编号为 1 到 n)以及 m(1≤m≤106) 条参考文献引用关系。目前小 K 已经打开了编号为 1 的一篇文章,请帮助小 K 设计一种方法,使小 K 可以不重复、不遗漏的看完所有他能看到的文章。
这边是已经整理好的参考文献关系图,其中,文献 X→Y 表示文章 X 有参考文献 Y。不保证编号为 1 的文章没有被其他文章引用。

请对这个图分别进行 DFS 和 BFS,并输出遍历结果。如果有很多篇文章可以参阅,请先看编号较小的那篇(因此你可能需要先排序)。
输入格式
共 m+1 行,第 1 行为 2 个数,n 和 m,分别表示一共有 n(1≤n≤105) 篇文章(编号为 1 到 n)以及m(1≤m≤106) 条参考文献引用关系。
接下来 m 行,每行有两个整数 X,Y 表示文章 X 有参考文献 Y。
输出格式
共 2 行。
第一行为 DFS 遍历结果,第二行为 BFS 遍历结果。
输入输出样例
输入 #1复制
8 9
1 2
1 3
1 4
2 5
2 6
3 7
4 7
4 8
7 8
输出 #1复制
1 2 5 6 3 7 8 4
1 2 3 4 5 6 7 8
实现代码:
cpp
#include<iostream>
#include<vector>
#include<queue>
#include<algorithm>
using namespace std;
struct edge{
int u,v;
};
vector <int> e[100001];
vector <edge> s;
bool vis1[100001]={0},vis2[100001]={0};
bool cmp(edge x,edge y){
if(x.v==y.v)
return x.u<y.u;
else return x.v<y.v;
}
void dfs(int x){
vis1[x]=1;
cout<<x<<" ";
for(int i=0;i<e[x].size();i++){
int point=s[e[x][i]].v;
if(!vis1[point]){
dfs(point);
}
}
}
void bfs(int x){
queue <int> q;
q.push(x);
cout<<x<<" ";
vis2[x]=1;
while(!q.empty()){
int fro=q.front();
for(int i=0;i<e[fro].size();i++){
int point=s[e[fro][i]].v;
if(!vis2[point]){
q.push(point);
cout<<point<<" ";
vis2[point]=1;
}
}
q.pop();
}
}
int main(){
int n,m;
cin>>n>>m;
for(int i=0;i<m;i++){
int uu,vv;
cin>>uu>>vv;
s.push_back((edge){uu,vv});
}
sort(s.begin(),s.end(),cmp);
for(int i=0;i<m;i++)
e[s[i].u].push_back(i);
dfs(1);
cout<<endl;
bfs(1);
}
P3916 图的遍历
题目描述
给出 N 个点,M 条边的有向图,对于每个点 v,令 A(v) 表示从点 v 出发,能到达的编号最大的点。现在请求出 A(1),A(2),...,A(N) 的值。
输入格式
第 1 行 2 个整数 N,M,表示点数和边数。
接下来 M 行,每行 2 个整数 Ui,Vi,表示边 (Ui,Vi)。点用 1,2,...,N 编号。
输出格式
一行 N 个整数 A(1),A(2),...,A(N)。
输入输出样例
输入 #1复制
4 3
1 2
2 4
4 3
输出 #1复制
4 4 3 4
说明/提示
- 对于 60% 的数据,1≤N,M≤103。
- 对于 100% 的数据,1≤N,M≤105。
实现代码:
cpp
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e5+10;
vector<int> a[N];
int vis[N];
int maxn;
void dfs(int x,int maxt){
if(vis[x]) return;
vis[x]=maxt;
for(int j=0;j<a[x].size();j++){
if(vis[a[x][j]]==0) {
dfs(a[x][j],maxt);
}
}
}
signed main(){
int n,m;
cin>>n>>m;
for(int i=1;i<=m;i++) {
int x,y;
cin>>x>>y;
a[y].push_back(x);
}
for(int i=n;i>=1;i--){
dfs(i,i);
}
for(int i=1;i<=n;i++) cout<<vis[i]<<" ";
return 0;
}
P1113 [USACO02FEB] 杂务
题目描述
John 的农场在给奶牛挤奶前有很多杂务要完成,每一项杂务都需要一定的时间来完成它。比如:他们要将奶牛集合起来,将他们赶进牛棚,为奶牛清洗乳房以及一些其它工作。尽早将所有杂务完成是必要的,因为这样才有更多时间挤出更多的牛奶。
当然,有些杂务必须在另一些杂务完成的情况下才能进行。比如:只有将奶牛赶进牛棚才能开始为它清洗乳房,还有在未给奶牛清洗乳房之前不能挤奶。我们把这些工作称为完成本项工作的准备工作。至少有一项杂务不要求有准备工作,这个可以最早着手完成的工作,标记为杂务 1。
John 有需要完成的 n 个杂务的清单,并且这份清单是有一定顺序的,杂务 k (k>1) 的准备工作只可能在杂务 1 至 k−1 中。
写一个程序依次读入每个杂务的工作说明。计算出所有杂务都被完成的最短时间。当然互相没有关系的杂务可以同时工作,并且,你可以假定 John 的农场有足够多的工人来同时完成任意多项任务。
输入格式
第 1 行,一个整数 n (3≤n≤10,000),必须完成的杂务的数目;
第 2 至 n+1 行,每行有一些用空格隔开的整数,分别表示:
- 工作序号(保证在输入文件中是从 1 到 n 有序递增的);
- 完成工作所需要的时间 len (1≤len≤100);
- 一些必须完成的准备工作,总数不超过 100 个,由一个数字 0 结束。有些杂务没有需要准备的工作只描述一个单独的 0。
保证整个输入文件中不会出现多余的空格。
输出格式
一个整数,表示完成所有杂务所需的最短时间。
输入输出样例
输入 #1复制
7
1 5 0
2 2 1 0
3 3 2 0
4 6 1 0
5 1 2 4 0
6 8 2 4 0
7 4 3 5 6 0
输出 #1复制
23
实现代码:
cpp
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int n,l,t,ans[10005],maxans;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;++i){
scanf("%d",&i);
scanf("%d",&l);
int tmp=0;
while(scanf("%d",&t)&&t)
tmp=max(ans[t],tmp);
ans[i]=tmp+l;
maxans=max(ans[i],maxans);
}
printf("%d\n",maxans);
return 0;
}
P4017 最大食物链计数
题目背景
你知道食物链吗?Delia 生物考试的时候,数食物链条数的题目全都错了,因为她总是重复数了几条或漏掉了几条。于是她来就来求助你,然而你也不会啊!写一个程序来帮帮她吧。
题目描述
给你一个食物网,你要求出这个食物网中最大食物链的数量。
(这里的"最大食物链",指的是生物学意义上的食物链 ,即最左端是不会捕食其他生物的生产者,最右端是不会被其他生物捕食的消费者。)
Delia 非常急,所以你只有 1 秒的时间。
由于这个结果可能过大,你只需要输出总数模上 80112002 的结果。
输入格式
第一行,两个正整数 n、m,表示生物种类 n 和吃与被吃的关系数 m。
接下来 m 行,每行两个正整数,表示被吃的生物 A 和吃 A 的生物 B。
输出格式
一行一个整数,为最大食物链数量模上 80112002 的结果。
输入输出样例
输入 #1复制
5 7
1 2
1 3
2 3
3 5
2 5
4 5
3 4
输出 #1复制
5
说明/提示
各测试点满足以下约定:
| 测试点编号 | n | m |
|---|---|---|
| 1,2 | ≤40 | ≤400 |
| 3,4 | ≤100 | ≤2×103 |
| 5,6 | ≤103 | ≤6×104 |
| 7,8 | ≤2×103 | ≤2×105 |
| 9,10 | ≤5×103 | ≤5×105 |
对于 100% 的数据,1≤n≤5×103,1≤m≤5×105
【补充说明】
数据中不会出现环,满足生物学的要求。(感谢 @AKEE)
实现代码:
cpp
#include<bits/stdc++.h>
using namespace std;
const int maxn=1000001;
const int mod=80112002;
queue<int> q;
int d[maxn],in[maxn],eat[maxn],head[maxn];
int n,m,tot,ans;
struct E{
int to,nxt;
}e[maxn];
inline void add(int u,int v)
{
e[++tot].to=v;
e[tot].nxt=head[u];
head[u]=tot;
}
inline void topo()
{
for(int i=1;i<=n;i++)
{
if(in[i]==0)
{
q.push(i);
d[i]++;
}
}
while(!q.empty())
{
int p=q.front();
q.pop();
for(int i=head[p];i;i=e[i].nxt)
{
int go=e[i].to;
d[go]=(d[go]+d[p])%mod;
in[go]--;
if(in[go]==0)
q.push(go);
}
}
}
int main()
{
cin>>n>>m;
for(int i=1;i<=m;i++)
{
int a,b;
cin>>a>>b;
add(a,b);
in[b]++;
eat[a]++;
}
topo();
for(int i=1;i<=n;i++)
{
if(eat[i]==0)
ans=(ans+d[i])%mod;
}
cout<<ans;
}
P1807 最长路
题目描述
设 G 为有 n 个顶点的带权有向无环图,G 中各顶点的编号为 1 到 n,请设计算法,计算图 G 中 1,n 间的最长路径。
输入格式
输入的第一行有两个整数,分别代表图的点数 n 和边数 m。
第 2 到第 (m+1) 行,每行 3 个整数 u,v,w(u<v),代表存在一条从 u 到 v 边权为 w 的边。
输出格式
输出一行一个整数,代表 1 到 n 的最长路。
若 1 无法到达 n,请输出 −1。
输入输出样例
输入 #1复制
2 1
1 2 1
输出 #1复制
1
说明/提示
【数据规模与约定】
- 对于 20%的数据,n≤100,m≤103。
- 对于 40% 的数据,n≤103,m≤104。
- 对于 100% 的数据,1≤n≤1500,0≤m≤5×104,1≤u,v≤n,−105≤w≤105。
实现代码:
cpp
#include<bits/stdc++.h>
using namespace std;
int n,m,in[1505];
vector<int>g[1505];
vector<int>d[1505];
queue<int>q;
int v[1505];
int main()
{
cin>>n>>m;
for(int i=1;i<=m;i++)
{
int ff,tt,dd;
cin>>ff>>tt>>dd;
g[ff].push_back(tt);
d[ff].push_back(dd);
in[tt]++;
}
for(int i=2;i<=n;i++)
{
v[i]=-1e9;
if(!in[i]) q.push(i);
}
while(!q.empty())
{
int x=q.front();
q.pop();
for(int i=0;i<g[x].size();i++)
if(!--in[g[x][i]]) q.push(g[x][i]);
}
q.push(1);
while(!q.empty())
{
int x=q.front();
q.pop();
for(int i=0;i<g[x].size();i++)
{
if(v[g[x][i]]<v[x]+d[x][i]) v[g[x][i]]=v[x]+d[x][i];
if(!--in[g[x][i]]) q.push(g[x][i]);
}
}
if(v[n]==-1e9) cout<<"-1";
else cout<<v[n];
return 0;
}