week 9 图论2

P5318 【深基18.例3】查找文献

小K 喜欢翻看洛谷博客获取知识。每篇文章可能会有若干个(也有可能没有)参考文献的链接指向别的博客文章。小K 求知欲旺盛,如果他看了某篇文章,那么他一定会去看这篇文章的参考文献(如果他之前已经看过这篇参考文献的话就不用再看它了)。

假设洛谷博客里面一共有 n(n≤10^5) 篇文章(编号为 1 到 n)以及m(m≤10^6) 条参考文献引用关系。目前小 K 已经打开了编号为 1 的一篇文章,请帮助小 K 设计一种方法,使小 K 可以不重复、不遗漏的看完所有他能看到的文章。

这边是已经整理好的参考文献关系图,其中,文献 X → Y 表示文章 X 有参考文献 Y。不保证编号为 1 的文章没有被其他文章引用。

请对这个图分别进行 DFS 和 BFS,并输出遍历结果。如果有很多篇文章可以参阅,请先看编号较小的那篇(因此你可能需要先排序)。

输入格式

共 m+1 行,第 1 行为 2 个数,n 和 m,分别表示一共有 n(n≤10^5) 篇文章(编号为 1 到 n)以及m(m≤10^6) 条参考文献引用关系。

接下来 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 

很有特色的一道存图题,既要我们dfs它,又要我们bfs它,所以,我就先正常存图进去,然后再对图进行dfs和bfs;题目也提示了我们要对其进行排序,我们就对sort函数进行一个cmp的重置;

#include <bits/stdc++.h>
using namespace std;
struct edge{
	int u,v;//u为起点,v为终点 
};
int n,m;
vector<int> q[100005];
bool vis1[100005],vis2[100005];//vis1用于dfs,vis2用于bfs 
vector<edge> s;
bool cmp(edge a,edge b){//先看编号较小的文献  
	if(a.v==b.v) return a.u<b.u;
	else return a.v<b.v;//小于号从大到小排序
}
void dfs(int k){
	vis1[k]=1;
	cout<<k<<' ';
	for(int i=0;i<q[k].size();i++){
		if(!vis1[s[q[k][i]].v]){
			dfs(s[q[k][i]].v);//因为只需要dfs一次,所以就懒得重置vis1数组了 
		}
	}
}
void bfs(int h){
	queue<int> p;
	p.push(h);
	cout<<h<<' ';
	vis2[h]=1;
	while(!p.empty()){
		int qd=p.front();
		for(int i=0;i<q[qd].size();i++){
			int zd=s[q[qd][i]].v;
			if(!vis2[zd]){
				p.push(zd);
				cout<<zd<<' ';
				vis2[zd]=1;
			}
		}
		p.pop();
	} 
}
int main(){
	cin>>n>>m;
	for(int i=0;i<m;i++){
		int u1,v1;
		cin>>u1>>v1;
		s.push_back((edge){u1,v1});
	}
	sort(s.begin(),s.end(),cmp);
	for(int i=0; i<m;i++){
		q[s[i].u].push_back(i);//存图 ,对于起点,数组存下它的对应的终点 
	}
	dfs(1);
	cout<<endl;
	bfs(1);
	return 0;
}

U80592 【模板】floyd

题目背景

模板题,无背景

题目描述

给出n个点,m条边的无向图,求每个点到其他点的距离之和%998244354的值

输入格式

第一行两个数n,m含义如上 从第二行开始,共m行,每行三个数x,y,l,代表从x到y点的长度为l

输出格式

n行,每行一个数,第i行代表点i到其他点的距离之和

输入输出样例

输入 #1

2 1
1 2 4

输出 #1

4

4 

输入 #2

4 5
1 2 1
1 3 2
2 3 2
3 4 3
2 4 4

输出 #2

8
7
7
12

首先更具题目得知,这是一个无向图;

我们先对无向图进行存图; 

这边是一个floyd算法

而算法的具体思想为:

1 .邻接矩阵(二维数组)dist储存路径,数组中的值开始表示点点之间初始直接路径,最终是点点之间的最小路径,有两点需要注意的,第一是如果没有直接相连的两点那么默认为一个很大的值(不要因为计算溢出成负数),第二是自己和自己的距离要为0。

2 .从第1个到第n个点依次加入松弛计算,每个点加入进行试探枚举是否有路径长度被更改(自己能否更新路径)。顺序加入(k枚举)松弛的点时候,需要遍历图中每一个点对(i,j双重循环),判断每一个点对距离是否因为加入的点而发生最小距离变化,如果发生改变(变小),那么两点(i,j)距离就更改。

dp[i][j]=min(dp[i][j],dp[i][k]+dp[k][j])

#include <bits/stdc++.h>
#define int long long
using namespace std;
int maxx=0x3f3f3f3f,f[5001][5001];
const int mod=99824435;
signed main(){
        int n,m;
        cin>>n>>m;
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                    f[i][j]=maxx;
                    f[i][i]=0;
            }
        }
        for(int i=1;i<=m;i++){
                int x,y,l;
                cin >> x >> y >> l;
                f[x][y]=min(l,f[x][y]);
                f[y][x]=min(l,f[y][x]);
        }
        for(int k=1;k<=n;k++)
            for(int i=1;i<=n;i++)
                for(int j=1;j<=n;j++)
                    if(f[i][k]+f[k][j]<f[i][j])
                        f[i][j]=(f[i][k]+f[k][j]);
 
        for(int i=1;i<=n;i++){
          int sum=0;
          for(int j=1;j<=n;j++)
          	if(f[i][j]!=maxx) sum=(sum+f[i][j])%mod;
          cout<<sum<<endl;
  		}   
    return 0;
}

P4779 【模板】单源最短路径(标准版)

 

题目背景

2018 年 7 月 19 日,某位同学在 NOI Day 1 T1 归程 一题里非常熟练地使用了一个广为人知的算法求最短路。

然后呢?

100→60;

Ag→Cu;

最终,他因此没能与理想的大学达成契约。

小 F 衷心祝愿大家不再重蹈覆辙。

题目描述

给定一个 n 个点,m 条有向边的带非负权图,请你计算从 s 出发,到每个点的距离。

数据保证你能从 s 出发到任意点。

输入格式

第一行为三个正整数n,m,s。 第二行起 m 行,每行三个非负整数ui​,vi​,wi​,表示从 ui​ 到 vi​ 有一条权值为 wi​ 的有向边。

输出格式

输出一行 n 个空格分隔的非负整数,表示 s 到每个点的距离。

输入输出样例

输入 #1

4 6 1
1 2 2
2 3 2
2 4 1
1 3 5
3 4 3
1 4 4

输出 #1

0 2 4 3

这题是一题我不会的题...

看了别人的题解,但也把它的做法优化了一下

把他的for循环改成了while循环...

就是菜,还得练

#include <bits/stdc++.h>
using namespace std;
struct Edge{
    int to,len;
};
int n,m,s,u,v,w;
vector<list<Edge> > dis;
struct Dot{
    int dist,t;
    bool operator < (Dot a)const{
        return dist>a.dist;//重载运算符 
    }
};
priority_queue<Dot> q;
vector<int> f;
int total;
int main(){
    cin>>n>>m>>s;
    dis.reserve(n+1);
    f.reserve(n+1);
    for(int i=0;i<=n;i++)
        dis.push_back(list<Edge>());
    for(int i=1;i<=m;i++){
        cin>>u>>v>>w;
        if(u!=v) dis[u].push_back( {v,w} );    
    }
    for(int i=0;i<=n;i++)
        f.push_back(2147483647);
    q.push( Dot{0,s} );
    total=n;
    while(total>0&&!q.empty()){
        Dot now=q.top(); q.pop();
        if(f[now.t]<=now.dist) continue;
        f[now.t]=now.dist; total--;
        for(list<Edge>::const_iterator i=dis[now.t].begin();i!=dis[now.t].end();i++)
             q.push( Dot{now.dist+i->len,i->to} );
    }
    for(int i=1;i<n;i++) cout<<f[i]<<' ';
    cout<<f[n]<<endl;
    return 0;
}  

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值