NOIP2018 复赛 图论模板集锦(精)
[图论模板]
最短路径问题(无负边权)
Time Limit: 1 Sec
Memory Limit: 128 MBDescription###
给定有向图 G,以及原点 S,请求出原点到所有点的最短路径。
Input###
输入文件的第一行包含两个整数 n, m,代表图中的顶点数和边数。
接下来 m 行,每行三个整数 u, v, w,代表一条从 u 指向 v,权值 为 w 的边。
最后一行为一个整数 S。
Output###
输出 n 个整数,依次代表 S 到 1, 2, . . . , n 的最短距离, S 到自己的距 离定义为 0,对于无法从 S 到达的点输出 -1。整数用一个空格隔开。
Sample Input###
3 3
1 2 1
2 3 2
1 3 1
1
Sample Output###
0 1 1
HINT
对于100%的数据,n ≤ 100000,m ≤ 800000,0 ≤ w ≤ 1000,请使用 dijkstra 算法。
AC代码
#include <cstdio>
#include <cstring>
#include <queue>
#define pa pair<int,int>
using namespace std;
const int N=1e5+5,inf=1e9;
int n,m;
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
struct edge{
int to,val,next;
}e[N<<3];
int cnt_edge,last[N];
inline void add_edge(int u,int v,int w){
e[++cnt_edge]=(edge){v,w,last[u]};last[u]=cnt_edge;
}
int dis[N];bool vis[N];
priority_queue<pa,vector<pa>,greater<pa> >q;
void Dijkstra(){
memset(vis,0,sizeof(vis));
for(int i=1;i<=n;i++)dis[i]=inf;
dis[1]=0;q.push(make_pair(0,1));
while(!q.empty()){
int u=q.top().second;q.pop();
vis[u]=1;
for(int i=last[u];i;i=e[i].next){
int v=e[i].to;
if(vis[v])continue;
if(dis[v]>dis[u]+e[i].val){
dis[v]=dis[u]+e[i].val;
q.push(make_pair(dis[v],v));
}
}
}
}
int main(){
n=read(),m=read();
for(int i=1;i<=m;i++){
int u=read(),v=read(),w=read();
add_edge(u,v,w);
}
Dijkstra();
for(int i=1;i<n;i++)
if(dis[i]==inf)printf("-1 ");
else printf("%d ",dis[i]);
if(dis[n]==inf)printf("-1\n");
else printf("%d\n",dis[n]);
return 0;
}
最短路径问题(二)(有负边权)
Time Limit: 1 Sec
Memory Limit: 128 MBDescription###
给定有向图 G,以及原点 S,请求出原点到所有点的最短路径。
Input###
输入文件的第一行包含两个整数 n, m,代表图中的顶点数和边数。
接下来 m 行,每行三个整数 u, v, w,代表一条从 u 指向 v,权值 为 w 的边。
最后一行为一个整数 S。
Output###
若图中存在负权环,则输出一行“ERROR”。
否则输出 n 个整数,依次代表 S 到 1, 2, . . . , n 的最短距离, S 到自己 的距离定义为 0,对于无法从 S 到达的点输出 -1。整数用一个空格隔开。
Sample Input###
Sample Output###
HINT
对于所有数据, n ≤ 1000, m ≤ 100000, −1000 ≤ w ≤ 1000。
AC代码
#include <cstdio>
#include <cstdlib>
#include <queue>
using namespace std;
const int N=1e3+5,inf=1e9;
int n,m;
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
struct edge{
int to,val,next;
}e[100005];
int cnt_edge,last[N];
void add_edge(int u,int v,int w){
e[++cnt_edge]=(edge){v,w,last[u]};last[u]=cnt_edge;
}
queue<int> q;
int dis[N],ins[N];
bool inq[N];
void SPFA(){
for(int i=1;i<=n;i++)dis[i]=inf;
dis[1]=0;
q.push(1);
inq[1]=1;ins[1]=1;
while(!q.empty()){
int u=q.front();q.pop();
inq[u]=0;
for(int i=last[u];i;i=e[i].next){
int v=e[i].to;
if(dis[v]>dis[u]+e[i].val){
dis[v]=dis[u]+e[i].val;
if(!inq[v]){
q.push(v);
inq[v]=1;
}
ins[v]++;
if(ins[v]==n){
puts("ERROR");
exit(0);
}
}
}
}
}
int main(){
n=read(),m=read();
for(int i=1;i<=m;i++){
int u=read(),v=read(),w=read();
add_edge(u,v,w);
}
SPFA();
for(int i=1;i<n;i++)
if(dis[i]==inf)printf("-1 ");
else printf("%d ",dis[i]);
if(dis[n]==inf)printf("-1\n");
else printf("%d\n",dis[n]);
return 0;
}
最小圈
Time Limit: 1 Sec
Memory Limit: 128 MBDescription###
给定一个带权无向图 G = (V, E),请求出其中的最小圈。圈定义为 顶点序列 (v1,v2,...,vp,vp+1),满足 vi = vj(i = j), v1 = vp+1,且边 (vi, vi+1) ∈ E。
Input###
第一行为两个整数 n,m,代表图的顶点数和边数。
接下来 m 行,每行三个整数 u,v,w,描述一条连接 u 和 v,权值为 w 的边。
Output###
输出一个整数,即图中的最小圈。数据保证图中至少存在一个圈。
Sample Input###
6 7
1 2 1
2 4 2
4 6 3
5 6 4
3 5 5
3 4 2
1 3 6
Sample Output###
11
HINT
对于 100% 的数据, n ≤ 400, w ≤ 100000
利用 Floyd 算法,求得环中的点最大编号为 1, 2, . . . , n 时的最小环。
AC代码
#include <cstdio>
#include <algorithm>
using namespace std;
const int inf=1e8;
int n,m,mn;
int mp[405][405],dis[405][405];
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int main(){
n=read(),m=read();
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
dis[i][j]=dis[j][i]=mp[i][j]=mp[j][i]=inf;
for(int i=1;i<=m;i++){
int u=read(),v=read(),w=read();
dis[u][v]=dis[v][u]=mp[u][v]=mp[v][u]=min(dis[u][v],w);
}
mn=inf;
for(int k=1;k<=n;k++){
for(int i=1;i<k;i++)
for(int j=i+1;j<k;j++)
mn=min(mn,dis[i][j]+mp[i][k]+mp[k][j]);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
}
printf("%d\n",mn);
return 0;
}
公路修建(有坑
有负边权的最大生成树)Time Limit: 1 Sec
Memory Limit: 128 MBDescription###
有 n 个城市需要用道路连接起来,现在有 m 条道路的修建方案,每 个方案的实施可以带来一定的利润,你的任务是确定实施的方案,使得任 意两个城市都连通,并且获得的利润最大。
Input###
第一行为两个整数 n, m,含义如题中所述。n ≤ 10000, m ≤ 200000。
接下来有 m 行,每行三个整数 u, v, w,代表在 u 和 v 之间修建一 条道路可以获得 w 的利润。注意,修建一条道路可能会导致亏损,这时我 们用负的利润表示。
Output###
输出一行,代表能够获得的最大利益。
Sample Input###
3 3
1 2 -1
2 3 -2
1 3 -1
Sample Output###
-2
HINT
AC代码
#include <cstdio>
#include <algorithm>
using namespace std;
int n,m,cnt,ans,fa[10005];
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
struct edge{
int u,v,w;
}e[200005];
bool cmp(edge a,edge b){
return a.w>b.w;
}
int find(int x){
while(x!=fa[x])x=fa[x]=fa[fa[x]];
return x;
}
int main(){
n=read(),m=read();
for(int i=1;i<=n;i++)
fa[i]=i;
for(int i=1;i<=m;i++){
int u=read(),v=read(),w=read();
if(w>=0){
ans+=w;
int fx=find(u);
int fy=find(v);
if(fx!=fy)fa[fx]=fy;
}else e[++cnt]=(edge){u,v,w};
}
sort(e+1,e+cnt+1,cmp);
for(int i=1;i<=cnt;i++){
int fx=find(e[i].u);
int fy=find(e[i].v);
if(fx!=fy){
ans+=e[i].w;
fa[fx]=fy;
}
}
printf("%d\n",ans);
return 0;
}
1504: 刻录光盘 (求强连通分量)
Time Limit: 1 Sec
Memory Limit: 128 MBDescription###
在 JSOI2005 夏令营快要结束的时候,很多营员提出来要把整个夏令 营期间的资料刻录成一张光盘给大家,以便大家回去后继续学习。组委会 觉得这个主意不错!可是组委会一时没有足够的空光盘,没法保证每个人 都能拿到刻录上资料的光盘,又来不及去买了,怎么办呢?!
组委会把这个难题交给了 LHC,LHC 分析了一下所有营员的地域关 系,发现有些营员是一个城市的,其实他们只需要一张就可以了,因为一 个人拿到光盘后,其他人可以带着 U 盘之类的东西去拷贝啊!
可是,LHC 调查后发现,由于种种原因,有些营员并不是那么的合 作,他们愿意某一些人到他那儿拷贝资料,当然也可能不愿意让另外一些 人到他那儿拷贝资料,这与我们 JSOI 宣扬的团队合作精神格格不入!
现在假设总共有 N 个营员(2<=N<=2000),每个营员的编号为 1 N。 LHC 给每个人发了一张调查表,让每个营员填上自己愿意让哪些人到他那 儿拷贝资料。当然,如果 A 愿意把资料拷贝给 B,而 B 又愿意把资料拷 贝给 C,则一旦 A 获得了资料,则 B,C 都会获得资料。
现在,请你编写一个程序,根据回收上来的调查表,帮助 LHC 计算 出组委会至少要刻录多少张光盘,才能保证所有营员回去后都能得到夏令 营资料?
Input###
先是一个数 N,接下来的 N 行,分别表示各个营员愿意把自己获得的 资料拷贝给其他哪些营员。即输入数据的第 i+1 行表示第 i 个营员愿意把 资料拷贝给那些营员的编号,以一个 0 结束。如果一个营员不愿意拷贝资 料给任何人,则相应的行只有 1 个 0,一行中的若干数之间用一个空格隔 开。
Output###
一个正整数,表示最少要刻录的光盘数。
Sample Input###
5
2 4 3 0
4 5 0
0
0
1 0
Sample Output###
1
HINT
AC代码
#include <cstdio>
#include <algorithm>
using namespace std;
const int N=2005;
int n,m,num,ans;
int U[N*N],V[N*N],in[N];
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
struct edge{
int to,next;
}e[N*N],_e[N*N];
int cnt_edge,last[N];
void add_edge(int u,int v){
e[++cnt_edge]=(edge){v,last[u]};last[u]=cnt_edge;
}
int _cnt_edge,_last[N];
void _add_edge(int u,int v){
_e[++_cnt_edge]=(edge){v,_last[u]};_last[u]=_cnt_edge;
}
int Time,top,col_num,col[N],dfn[N],low[N],q[N];
bool ins[N];
void Tarjan(int u){
dfn[u]=low[u]=++Time;
q[++top]=u;ins[u]=1;
for(int i=last[u];i;i=e[i].next){
int v=e[i].to;
if(!dfn[v]){
Tarjan(v);
low[u]=min(low[u],low[v]);
}else if(ins[v])
low[u]=min(low[u],dfn[v]);
}
if(dfn[u]==low[u]){
++col_num;
while(q[top]!=u){
col[q[top]]=col_num;
ins[q[top]]=0;
top--;
}
col[q[top]]=col_num;
ins[q[top]]=0;
top--;
}
}
int main(){
n=read();
for(int i=1;i<=n;i++){
int x=read();
while(x!=0){
++num;
U[num]=i;
V[num]=x;
add_edge(i,x);
x=read();
}
}
for(int i=1;i<=n;i++)
if(!dfn[i])Tarjan(i);
for(int i=1;i<=num;i++)
if(col[U[i]]!=col[V[i]])
in[col[V[i]]]++;
for(int i=1;i<=col_num;i++)
if(!in[i])ans++;
printf("%d\n",ans);
return 0;
}
割点和桥
Time Limit: 1 Sec
Memory Limit: 128 MBDescription###
给定连通无向图 G,请求出 G 中的割点和桥的个数。
Input###
第一行两个整数 n,m,代表图的顶点数和边数。 接下来 m 行,每行两个整数 u, v,描述一条无向边。 N ≤ 50000, m ≤ 200000
Output###
输出两个整数,先输出割点的数量和再输出桥的数量,用一个空格隔 开。
Sample Input###
4 5
1 2
1 2
2 3
2 3
3 4
Sample Output###
2 1
HINT
AC代码
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
const int N=5e4+5;
int n,m;
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
struct edge{
int to,next;
}e[400005];
int cnt_edge=1,last[N];
inline void add_edge(int u,int v){
e[++cnt_edge]=(edge){v,last[u]};last[u]=cnt_edge;
}
struct _edge{
int from,to;
};
int Time,dfn[N],low[N];
void init(){
Time=0;
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
}
bool isCut_point[N];
void Tarjan_cut_point(int u){
dfn[u]=low[u]=++Time;
int child=0;
for(int i=last[u];i;i=e[i].next){
int v=e[i].to;
if(!dfn[v]){
Tarjan_cut_point(v);
low[u]=min(low[u],low[v]);
child++;
if(u!=1 && low[v]>=dfn[u])isCut_point[u]=1;
}else
low[u]=min(low[u],dfn[v]);
}
if(u==1 && child>1)isCut_point[u]=1;
}
vector<_edge> bridge;
void Tarjan_bridge(int u,int from){
dfn[u]=low[u]=++Time;
for(int i=last[u];i;i=e[i].next){
int v=e[i].to;
if(i==(from^1))continue;
if(!dfn[v]){
Tarjan_bridge(v,i);
low[u]=min(low[u],low[v]);
if(low[v]>dfn[u])bridge.push_back((_edge){u,v});
}else
low[u]=min(low[u],dfn[v]);
}
}
int main(){
n=read(),m=read();
for(int i=1;i<=m;i++){
int u=read(),v=read();
add_edge(u,v);
add_edge(v,u);
}
init();
Tarjan_cut_point(1);
init();
Tarjan_bridge(1,0);
int Cut_point=0;
for(int i=1;i<=n;i++)
if(isCut_point[i])Cut_point++;
printf("%d %d\n",Cut_point,(int)bridge.size());
return 0;
}
未完待续
作者:skl_win
出处:https://www.cnblogs.com/shaokele/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。