目录
- P5318 【深基18.例3】查找文献【★ 图的遍历】
- P3916 图的遍历【★★ 求每一个点可以到达的最大的点 反向建图】
- P1113 杂务【★ ★ 拓扑排序 求完成所有杂务所需的最短时间】
- P4017 最大食物链计数【★ ★ 拓扑排序 求链数】
- P1807 最长路【★ ★ 最长路】
- P2853 [USACO06DEC]Cow Picnic S【★ 图的遍历】
- P3371 【模板】单源最短路径(弱化版)【★ 最短路】
- P1629 邮递员送信【★★ 一去一回的最短路 反向建图】
- P4779 【模板】单源最短路径(标准版)【★ 最短路】
- P1144 最短路计数【★★ 统计最短路的数量】
- P3366 【模板】最小生成树【★ 最小生成树】
- P2872 [USACO07DEC]Building Roads S【★★ 最小生成树】
- P1991 无线通讯网【★★ 最小生成树】
- P1396 营救【★★ 最小生成树 让一个点到另一个点的最大值最小】
- P2121 拆地毯【★★ 最小生成树的变种】
- P1194 买礼物【★★ 最小生成树】
- P1195 口袋的天空【★★ 最小生成树】
P5318 【深基18.例3】查找文献【★ 图的遍历】
考察的就是基础的图的遍历。
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
vector<int>ve[N];
int n,m,st[N];
void dfs(int u)
{
cout<<u<<" ";
st[u]=1;
for(int i=0;i<ve[u].size();i++)
if(!st[ve[u][i]]) dfs(ve[u][i]);
}
void bfs(int u)
{
memset(st,0,sizeof st);
queue<int>q; q.push(u); st[u]=1;
while(q.size())
{
u=q.front(); q.pop();
cout<<u<<" ";
for(int i=0;i<ve[u].size();i++)
if(!st[ve[u][i]]) q.push(ve[u][i]),st[ve[u][i]]=1;
}
}
int main(void)
{
cin>>n>>m;
for(int i=0;i<m;i++)
{
int a,b; scanf("%d%d",&a,&b);
ve[a].push_back(b);
}
for(int i=1;i<=n;i++) sort(ve[i].begin(),ve[i].end());//排序
dfs(1);
puts("");
bfs(1);
return 0;
}
P3916 图的遍历【★★ 求每一个点可以到达的最大的点 反向建图】
求一个点到最大的点,等价于最大的点到它可以到达的点。
故我们可以反向建图。从大到小枚举所有的点,如果该点遍历过了,说明有一个更大的点之前来过(因为我们是先枚举大结点),故遍历过的可以不用遍历。那么总的时间复杂度就是线性的O(m),完全可以过。
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int h[N],e[N],ne[N],idx;
int st[N],ans[N],n,m;
void add(int a,int b)
{
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void dfs(int u,int maxv)
{
ans[u]=maxv;
st[u]=1;
for(int i=h[u];i!=-1;i=ne[i])
{
int j=e[i];
if(!st[j]) dfs(j,max(maxv,j));//没有遍历过
}
}
int main(void)
{
memset(h,-1,sizeof h);
cin>>n>>m;
for(int i=0;i<m;i++)
{
int a,b; scanf("%d%d",&a,&b);
add(b,a);//反向建图
}
for(int i=n;i>=1;i--) if(!st[i]) dfs(i,i);//从大到小枚举
for(int i=1;i<=n;i++) cout<<ans[i]<<" ";
return 0;
}
P1113 杂务【★ ★ 拓扑排序 求完成所有杂务所需的最短时间】
比较容易的想到的是拓扑排序。这好像是拓扑排序的一个非常经典的模型。
但问题是如何求,不难想到的是答案就是最晚的点的结束时间。
例:A完成需要先完成B,C。 故A结束的时间=A需要花费的时间+max(B,C)花费的时间
#include<bits/stdc++.h>
using namespace std;
const int N=1e5*6+10;
typedef long long int LL;
int h[N],e[N],ne[N],idx;
LL w[N],st[N],cnt[N],f[N],n,ans;
void add(int a,int b)
{
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void topsort()
{
queue<int>q;
for(int i=1;i<=n;i++) if(!cnt[i]) q.push(i),st[i]=1;
while(q.size())
{
int u=q.front(); q.pop();
for(int i=h[u];i!=-1;i=ne[i])
{
int j=e[i];
f[j]=max(f[j],w[u]);//求需要完成的先前任务的最大时间
if(--cnt[j]==0)
{
if(!st[j]) q.push(j),st[j]=1,w[j]+=f[j];
};
}
}
for(int i=1;i<=n;i++) ans=max(ans,w[i]);//枚举所有的点,存最晚结束的时间
cout<<ans;
}
int main(void)
{
memset(h,-1,sizeof h);
cin>>n;
for(int i=1;i<=n;i++)
{
int x,id;
cin>>id>>x;
w[id]=x;
while(cin>>x,x) add(x,id),cnt[id]++;
}
topsort();
return 0;
}
P4017 最大食物链计数【★ ★ 拓扑排序 求链数】
既然 食物链中的生物 可以看成 节点,那么 最佳生产者 的入度一定为 0, 而 最佳消费者 的出度也为 0。
例子:B->A, C->A, 故以A为终点的条数等于 到B点的条数+到C点的条数。这是一个递推累加的过程。
#include<bits/stdc++.h>
using namespace std;
typedef long long int LL;
const int N=1e5*5+10;
const int mod=80112002;
int h[N],e[N],ne[N],idx;
int st[N],in[N],out[N],n,m;
LL sum[N],ans;
void add(int a,int b)
{
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void topsort()
{
queue<int>q;
for(int i=1;i<=n;i++) if(in[i]==0) q.push(i),st[i]=1,sum[i]=1;
while(q.size())
{
int u=q.front(); q.pop();
for(int i=h[u];i!=-1;i=ne[i])
{
int j=e[i];
sum[j]=(sum[j]+sum[u])%mod;
if(--in[j]==0)
{
if(!st[j]) q.push(j),st[j]=1;
}
}
}
}
int main(void)
{
memset(h,-1,sizeof h);
cin>>n>>m;
for(int i=0;i<m;i++)
{
int a,b; scanf("%d%d",&a,&b);
add(a,b);
out[a]++,in[b]++;
}
topsort();
for(int i=1;i<=n;i++) if(out[i]==0) ans=(ans+sum[i])%mod;//是一条链的结尾
cout<<ans;
return 0;
}
P1807 最长路【★ ★ 最长路】
传统的是最短路,那么如何求最长路呢?只需将边权乘以(-1) 就变成了求最短路。
最后结果记得再乘以(-1)回来就是结果。
#include<bits/stdc++.h>
using namespace std;
const int N=1e5*5+10;
int h[N],e[N],w[N],ne[N],idx;
int st[N],dist[N],n,m;
void add(int a,int b,int c)
{
e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
void spfa()
{
memset(dist,0x3f,sizeof dist);
dist[1]=0;
queue<int>q; q.push(1);
st[1]=1;
while(q.size())
{
int u=q.front(); q.pop();
st[u]=0;
for(int i=h[u];i!=-1;i=ne[i])
{
int j=e[i];
if(dist[j]>dist[u]+w[i])
{
dist[j]=dist[u]+w[i];
if(!st[j]) q.push(j),st[j]=1;
}
}
}
}
int main(void)
{
memset(h,-1,sizeof h);
cin>>n>>m;
for(int i=0;i<m;i++)
{
int a,b,c; scanf("%d%d%d",&a,&b,&c);
add(a,b,-c);
}
spfa();
if(dist[n]==0x3f3f3f3f) puts("-1");
else cout<<-dist[n];
return 0;
}
P2853 [USACO06DEC]Cow Picnic S【★ 图的遍历】
对于每一个奶牛所在的农场遍历一遍图。
吐过最后某个农场的计数是k则累加即可。
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
vector<int>ve[N];
int st[N],cnt[N],a[N],k,n,m;
void dfs(int u)
{
cnt[u]++,st[u]=1;
for(int i=0;i<ve[u].size();i++)
if(!st[ve[u][i]]) dfs(ve[u][i]);
}
int main(void)
{
cin>>k>>n>>m;
for(int i=1;i<=k;i++) cin>>a[i];
for(int i=0;i<m;i++)
{
int a,b; cin>>a>>b;
ve[a].push_back(b);
}
for(int i=1;i<=k;i++)
{
memset(st,0,sizeof st);
dfs(a[i]);
}
int ans=0;
for(int i=1;i<=n;i++) if(cnt[i]==k) ans++;
cout<<ans;
return 0;
}
P3371 【模板】单源最短路径(弱化版)【★ 最短路】
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;
const int N=1e4+10;
const int M=1e5*5+10;
int h[N],e[M],ne[M],w[M],idx;
int st[N],dist[N],n,m,s;
void add(int a,int b,int c)
{
e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
void Dijkstra(int s)
{
for(int i=1;i<=n;i++) dist[i]=(1ll<<31)-1;
dist[s]=0;
priority_queue<pii,vector<pii>,greater<pii> >q; q.push({
0,s});
while(q.size())
{
auto temp=q.top(); q.pop();
int u=temp.second;
if(st[u]) continue;
st[u]=1;
for(int i=h[u];i!=-1;i=ne[i])
{
int j=e[i];
if(dist[j]>dist[u]+w[i])
{
dist[j]=dist[u]+w[i];
q.push({
dist[j],j});
}
}
}
}
int main(void)
{
memset(h,-1,sizeof h);
scanf("%d%d%d",&n,&m,&s);
for(int i=1;i<=m;i++)
{
int a,b,c; scanf("%d%d%d",&a,&b,&c);
add(a,b,c);
}
Dijkstra(s);
for(int i=1;i<=n;i++) cout<<dist[i]<<" ";
}
P1629 邮递员送信【★★ 一去一回的最短路 反向建图】
问题在于如何求每一个点回去的时候的最短路。
考虑到回去的最短路,故反向建图这样就可以正着跑了。
#include<bits/stdc++.h>
using namespace std;
const int N=1e5*3+10;
typedef pair<int,int> PII;
int h[N],e[N],w[N],ne[N],idx;
int dist[N],st[N],n,m;
void add(int a,int b,int c)
{
e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
void Dijkstra(int x)
{
memset(dist,0x3f,sizeof dist);
memset(st,0,sizeof st);
dist[x]=0;
priority_queue<PII,vector<PII>,greater<PII> >q; q.push({
0,x});
while(q.size())
{
auto temp=q.top(); q.pop();
int u=temp.second;
if(st[u]) continue;
st[u]=1;
for(int i=h[u];i!=-1;i=ne[i])
{
int j=e[i];
if(dist[j]>dist[u]+w[i])
{
dist[j]=dist[u]+w[i];
q.push({
dist[j],j});
}
}
}
}
int main(void)
{
memset(h,-1,sizeof h);
cin>>n>>m;
for(int i=0;i<m;i++)
{
int a,b,c; scanf("%d%d%d",&a,&b,&c);
add(a,b,c);
add(b+n,a+n,c);//反向建图
}
Dijkstra(1);
int sum=0;
for(int i=2;i<=n;i++) sum+=dist[i];
Dijkstra(1+n);
for(int i=2;i<=n;i++) sum+=dist[i+n];
cout<<sum<<endl;
return 0;
}
P4779 【模板】单源最短路径(标准版)【★ 最短路】
#include<cstdio>
#include<iostream>
#include<cstring>
#include<queue>
#include<map>
using namespace std;
const int N=1e5+10;
const int M=1e5*2+10;
typedef pair<int,int> PII;
int h[N],e[M],ne[M],w[M],idx;
int dist[N];
bool st[N];
int n,m,s;
void add(int a,int b,int c)
{
e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
void spfa(int s)
{
memset(dist,0x3f,sizeof dist);
dist[s]=0;
st[s]=true;
queue<int> q; q.push(s);
while(q.size())
{
int t=q.front(); q.pop();
st[t]=false;
for(int i=h[t];i!=-1;i=ne[i])
{
int j=e[i];
if(dist[j]>dist[t]+w[i])
{
dist[j]=dist[t]+w[i];
if(!st[j]) q.push(j),st[j]=true;
}
}
}
}
int main(void)
{
memset(h,-1,sizeof h);
scanf("%d%d%d",&n,&m,&s);
while(m--)
{
int a,b,c; scanf("%d%d%d",&a,&b,&c);
add(a,b,c);
}
spfa(s);
for(int i=1;i<=n;i++) cout<<dist[i]<<" ";
cout<<endl;
return 0;
}
P1144 最短路计数【★★ 统计最短路的数量】
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
const int M=1e6*4+10;
const int mod=100003;
int h[N],e[M],ne[M],idx;
int cnt[N],dist[N],n,m;
void add(int a,int b) {
e[idx]=b,ne[idx]=h[a],h[a]=idx++;}
void bfs()
{
memset(dist,0x3f,sizeof dist);
dist[1]=0,cnt[1]=1;
queue<int>q; q.push(1);
while(q.size())
{
int u=q.front(); q.pop();
for(int i=h[u];i!=-1;i=ne[i])
{
int j=e[i];
if(dist[j]>dist[u]+1)
{
dist[j]=dist[u]+1;
cnt[j]=cnt[u];
q.push(j);
}else if(dist[j]==dist[u]+1)
{
cnt[j]=(cnt[j]+cnt[u])%mod;
}
}
}
}
int main(void)
{
memset(h,-1,sizeof h);
cin>>n>>m;
for(int i=0;i<m;i++)
{
int a,b; cin>>a>>b;
add(a,b),add(b,a);
}
bfs();
for(int i=1;i<=n;i++) cout<<cnt[i]<<endl;
return 0;
}
P3366 【模板】最小生成树【★ 最小生成树】
#include<bits/stdc++.h>
using namespace std;
const int N=1e4+10;
int p[N],n,m;
struct node{
int a,b,c;};
bool cmp(node a,node b){
return a.c<b.c;}
vector<node>ve;
int find(int x)
{
if(x!=p[x]) p[x]=find(p[x]);
return p[x];
}
int f()
{
int res=0,cnt=0;
for(int i=1;i<=n;i++) p[i]=i;
sort(ve.begin(),ve.end(),cmp);
for(int i=0;i<ve.size();i++)
{
int a=ve[i].a,b=ve[i].b,c=ve[i].c;
if(find(a)!=find(b))
{
p[find(a)]=find(b);
res+=c;
cnt++;
}
}
if(cnt==n-1) return res;
else return -1;
}
int main(void)
{
cin>>n>>m;
while(m--)
{
int a,b,c; cin>>a>>b>>c;
ve.push_back({
a,b,c});
}
int ans=f();
if(ans==-1) puts("orz");
else cout<<ans;
return 0;
}
P2872 [USACO07DEC]Building Roads S【★★ 最小生成树】
#include<bits/stdc++.h>
using namespace std;
const int N=1e4+10;
typedef pair<double,double> PII;
struct node
{
int a,b;
double c;
};
bool cmp(node a,node b){
return a.c<b.c;}
int n,m,p[N];
vector<node>ve;
vector<PII>a;
int find(int x)
{
if(x!=p[x]) p[x]=find(p[x]);
return p[x];
}
double get(PII a,PII b)
{
double x=(a.first-b.first)*(a.first-b.first);
double y=(a.second-b.second)*(a.second-b.second);
return sqrt(x+y);
}
void solve()
{
double ans=0;
sort(ve.begin(),ve.end(),cmp);
for(int i=0;i<ve.size();i++)//最小生成树
{
int a=ve[i].a,b=ve[i].b;
double c=ve[i].c;
if(find(a)==find(b)) continue;
p[find(a)]=find(b);
ans+=c;
}
printf("%.2lf",ans);
}
int main(void)
{
cin>>n>>m;
for(int i=0;i<n;i++)
{
double x,y; cin>>x>>y;
a.push_back({
x,y});
}
for(int i=1;i<=n;i++) p[i]=i;
for(int i=0;i<m;i++)
{
int a,b; cin>>a>>b;
p[find(a)]=find(b);
}
for(int i=0;i<n;i++)
for(int j=i+1;j<n;j++)//存所有的边
{
double c=get(a[i],a[j]);
ve.push_back({
i+1,j+1,c});
}
solve();
return 0;
}
P1991 无线通讯网【★★ 最小生成树】
也就是说卫星电话是无需距离的。
我们先构造一个最小生成树,然后再减边。让其用卫星电话。
例如: 3给卫星电话 可以减2条边
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
const int N=1e5+10;
struct node{
int a,b;double c;};
bool cmp(node a,node b){
return a.c<b.c;}
int m,n,p[N];
vector<node>ve;
vector<PII>a;
int find(int x)
{
if(x!=p[x]) p[x]=find(p[x]);
return p[x];
}
double get(PII a,PII b)
{
int x=(a.first-b.first)*(a.first-b.first);
int y=(a.second-b.second)*(a.second-b.second);
return sqrt(x+y);
}
void sovle()
{
for(int i=1;i<=n;i++) p[i]=i;
vector<double>s;
sort(ve.begin(),ve.end(),cmp);
for(int i=0;i<ve.size();i++)
{
int a=ve[i].a,b=ve[i].b;
double c=ve[i].c;
if(find(a)==find(b)) continue;
s.push_back(c);
p[find(a)]=find(b);
}
sort(s.begin(),s.end());
printf("%.2lf",s[s.size()-1-(m-1)]);
}
int main(void)
{
cin>>m>>n;
for(int i=0;i<n;i++)
{
int x,y; cin>>x>>y;
a.push_back({
x,y});
}
for(int i=0;i<n;i++)
for(int j=i+1;j<n;j++)
{
double c=get(a[i],a[j]);
ve.push_back({
i+1,j+1,c});
}
sovle();
return 0;
}
P1396 营救【★★ 最小生成树 让一个点到另一个点的最大值最小】
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
struct node{
int a,b,c;};
bool cmp(node a,node b){
return a.c<b.c;}
int p[N],n,m,s,t;
vector<node>ve;
int find(int x)
{
if(x!=p[x]) p[x]=find(p[x]);
return p[x];
}
void solve()
{
for(int i=1;i<=n;i++) p[i]=i;
sort(ve.begin(),ve.end(),cmp);
for(int i=0;i<m;i++)
{
int a=ve[i].a,b=ve[i].b,c=ve[i].c;
p[find(a)]=find(b);
if(find(s)==find(t)) //说明联通了
{
cout<<c;
return;
}
}
}
int main(void)
{
cin>>n>>m>>s>>t;
for(int i=0;i<m;i++)
{
int a,b,c; cin>>a>>b>>c;
ve.push_back({
a,b,c});
}
solve();
return 0;
}
P2121 拆地毯【★★ 最小生成树的变种】
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int p[N],n,m,k;
struct node{
int a,b,c;};
bool cmp(node a,node b){
return a.c>b.c;}//从大到小排
vector<node>ve;
int find(int x)
{
if(x!=p[x]) p[x]=find(p[x]);
return p[x];
}
void solve()
{
for(int i=1;i<=n;i++) p[i]=i;
sort(ve.begin(),ve.end(),cmp);
int cnt=0,ans=0;
for(int i=0;i<ve.size();i++)
{
int a=ve[i].a,b=ve[i].b,c=ve[i].c;
if(find(a)==find(b)) continue;
ans+=c,cnt++;
p[find(a)]=find(b);
if(cnt>=k) break;
}
cout<<ans;
}
int main(void)
{
cin>>n>>m>>k;
for(int i=0;i<m;i++)
{
int a,b,c; cin>>a>>b>>c;
ve.push_back({
a,b,c});
}
solve();
return 0;
}
P1194 买礼物【★★ 最小生成树】
#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int price,n,g[N][N],p[N];
struct node{
int a,b,c;};
vector<node>ve;
bool cmp(node a,node b){
return a.c<b.c;}
int find(int x)
{
if(x!=p[x]) p[x]=find(p[x]);
return p[x];
}
void solve()
{
for(int i=1;i<=n;i++) p[i]=i;
sort(ve.begin(),ve.end(),cmp);
int cnt=1,w=price;
for(int i=0;i<ve.size();i++)
{
int a=ve[i].a,b=ve[i].b,c=ve[i].c;
if(find(a)==find(b)) continue;
if(c>=price) continue;//也就是说不如直接买
w+=c,cnt++;
p[find(a)]=find(b);
}
cout<<(n-cnt)*price+w;//(n-cnt) 就是还没买的
}
int main(void)
{
cin>>price>>n;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++) cin>>g[i][j];
for(int i=1;i<=n;i++)
for(int j=i;j<=n;j++)
if(g[i][j]) ve.push_back({
i,j,g[i][j]});
solve();
return 0;
}
P1195 口袋的天空【★★ 最小生成树】
先构造最小生成树,再减最大的边让其变成k个连通块
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
struct node{
int a,b,c;};
int p[N],n,m,k,ans;
bool cmp(node a,node b){
return a.c<b.c;}
vector<node>ve;
int find(int x)
{
if(x!=p[x]) p[x]=find(p[x]);
return p[x];
}
void kruskal()
{
for(int i=1;i<=n;i++) p[i]=i;
sort(ve.begin(),ve.end(),cmp);
vector<int>s;
int ans=0;
for(int i=0;i<ve.size();i++)
{
int a=ve[i].a,b=ve[i].b,c=ve[i].c;
if(find(a)==find(b)) continue;
p[find(a)]=find(b);
s.push_back(c);
ans+=c;
}
sort(s.begin(),s.end());
reverse(s.begin(),s.end());//让其从大到小排序
map<int,int>mp;
for(int i=1;i<=n;i++) mp[find(i)]++;//连通块的数量
if(mp.size()>k||n<k) puts("No Answer");
else
{
k=k-mp.size();//需要减几条边
for(int i=0;i<k;i++) ans-=s[i];
cout<<ans;
}
}
int main(void)
{
cin>>n>>m>>k;
for(int i=0;i<m;i++)
{
int a,b,c; cin>>a>>b>>c;
ve.push_back({
a,b,c});
}
kruskal();
return 0;
}