第73期:图论-2022/7/19学习报告

目录

1. 链式向前星(数组模拟链表实现邻接表)

2. 单源最短路径 Dijkstra算法(非负)(链式向前星+优先队列 优化)

3. SPFA算法(可负)(Shortest Path Fast Algorithm)

4. EX: POJ-3662-Telephone Lines

做法一:分层图最短路

做法二:二分答案+双端队列BFS

做法三:二分答案+dijkstra

5. EX CH6101 / P1073 最优贸易

6. EX BZOJ2200 [Usaco2011 Jan] 道路和航线

7. 任意两点间最短路径

8. EX POJ1094 Sorting It All Out

9. EX POJ1734 Sightseeing trip

10. POJ3613 EX Cow Relays


1. 链式向前星(数组模拟链表实现邻接表)

链式向前星的空间复杂度:O(n+m)

const int N=100010;//点
const int M=1000010;//边
int head[N],ver[M],edge[M],Next[M],d[N];//头节点,边的尾点,权值,指向的上一条边,点N到起点的距离
int n,m,tot;

//加入有向边(x,y),权值为z
void add(int x,int y,int z){
    ver[++tot]=y,edge[tot]=z; // 真实数据
    Next[tot]=head[x],head[x]=tot; //从表头x处插入
}

//初始化
void init(){
    tot=0;
    memset(head,0,sizeof head);
}

//访问从x出发的所有边
for(int i=head[x];i;i=Next[i]){
    int y=ver[i],z=edge[i];
    // 找到了一条有向边(x,y),权值为z
}

2. 单源最短路径 Dijkstra算法(非负)(链式向前星+优先队列 优化)

//dijkstra单源最短路径:链式向前星+优先队列 O((m+n)logn)
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
const int M=1000010;
const int INF=0x3f3f3f3f;
int head[N],ver[M],edge[M],Next[M],d[N];//头节点,边的尾点,权值,指向的上一条边,点N到起点的距离
bool v[N];
int n,m,tot;
priority_queue<pair<int,int> > pq;//大根堆,pair第二维为节点编号,第一维为dist的相反数(利用相反数变成小根堆)
void add(int x,int y,int z){
    ver[++tot]=y,edge[tot]=z,Next[tot]=head[x],head[x]=tot;
}
void init(){
    tot=0;
    memset(head,0,sizeof head);
}
void dijkstra(){
    int s=1; // 起点
    memset(d,0x3f,sizeof d); //dist数组
    memset(v,0,sizeof v); //节点标记
    d[s]=0;
    pq.push(make_pair(-d[s],s)); //起点入队
    while(pq.size()){
        int x=pq.top().second; pq.pop(); //取出堆顶
        if(v[x]) continue; //已经找到最短路
        v[x]=1;
        //扫描所有边
        for(int i=head[x];i;i=Next[i]){
            int y=ver[i],z=edge[i];
            if(d[y]>d[x]+z){
                //更新,把新的二元组插入堆
                d[y]=d[x]+z;
                pq.push(make_pair(-d[y],y));
            }
        }
    }
}
int main(){
    while(cin>>n>>m&&n&&m){
        init();
        for(int i=1;i<=m;i++){
            int x,y,z;
            cin>>x>>y>>z;
            add(x,y,z);
            add(y,x,z);
        }
        dijkstra();
        cout<<d[n]<<endl;
    }
    return 0;
}

3. SPFA算法(可负)(Shortest Path Fast Algorithm)

#include<bits/stdc++.h>
using namespace std;
const int INF=INT_MAX/10;
const int N=100010;
const int M=1000010;
int head[N],ver[M],edge[M],Next[M],d[N];//头节点,边的尾点,权值,指向的上一条边,点N到起点的距离
int Neg[M]; //判断负圈
int pre[N]; //记录前驱节点
bool inq[N]; //inq[i]=true表示节点i在队列中
int n,m,tot;
void print_path(int s,int t){ //打印从s到t的最短路径
    if(s==t){cout<<s; return;} //打印起点
    print_path(s,pre[t]); //先打印前一个点
    cout<<t; //后打印当前点,最后打印的是终点t
}
void init(){
    tot=0;
    memset(head,0,sizeof head);
}
void add(int x,int y,int z){
    ver[++tot]=y,edge[tot]=z,Next[tot]=head[x],head[x]=tot;
}
int spfa(int s){//s是起点
    memset(Neg,0,sizeof Neg);
    Neg[s]=1;
    for(int i=1;i<=n;i++){d[i]=INF,inq[i]=false;} //初始化
    d[s]=0; //起点到自己距离为0
    queue<int> q;
    q.push(s);
    inq[s]=true; //起点进入队列
    while(q.size()){
        int u=q.front(); q.pop(); //队头出队
        inq[u]=false; 
        for(int i=head[u];i;i=Next[i]){
            int v=ver[i],w=edge[i];
            if(d[v]>d[u]+w){ //u的第i个邻居v,它借道u,到s更近
                d[v]=d[u]+w; //更新第i个邻居到s的距离
                pre[v]=u;
                if(!inq[v]){//邻居v更新状态了,但是它不在队列中,把它放进队列
                    inq[v]=true;
                    q.push(v);
                    Neg[v]++;
                    if(Neg[v]>n) return 1;  //出现负圈
                }
            }
        }
    }
    cout<<d[n]<<endl; //从s到n的最短距离
    //print_path(s,n);
    return 0;
}
int main(){
    while(cin>>n>>m&&n&&m){
        init();
        while(m--){
            int u,v,w;
            cin>>u>>v>>w;
            add(u,v,w);
            add(v,u,w);
        }
        spfa(1);
    }   
    return 0;
}

4. EX: POJ-3662-Telephone Lines

做法一:分层图最短路

#include <iostream>
#include <cstring>
#include <vector>
#include <queue>
using namespace std;
int n, p, k, vis[1010][1010], d[1010][1010];
typedef pair<int,int> P;
struct Nod {
    int v, c, w;
    bool operator < (const Nod &a) const {
        return w > a.w;
    }
};
vector<P> G[1010];
void spfa(int s) {
    memset(d,-1,sizeof(d));memset(vis,0,sizeof(vis));
    d[s][0] = 0;
    priority_queue<Nod> que;
    que.push((Nod){s,0,0});
    while(!que.empty()) {
        int u = que.top().v, c = que.top().c; que.pop();
        if(vis[u][c])continue;
        vis[u][c] = 1;
        for(int i = 0; i < G[u].size(); i ++) {
            int v = G[u][i].first, w = G[u][i].second;
            if(!vis[v][c] && (d[v][c] == -1 || d[v][c] > max(d[u][c],w))) {
                d[v][c] = max(d[u][c], w);
                que.push((Nod){v,c,d[v][c]});
            }
            if(c < k) {
                if(!vis[v][c+1] && (d[v][c+1] == -1 || d[v][c+1] > d[u][c])) {
                    d[v][c+1] = d[u][c];
                    que.push((Nod){v,c+1,d[v][c+1]});
                }
            }
        }
    }
}
int main() {
    ios::sync_with_stdio(false);
    cin>>n>>p>>k;
    int s = 1, t = n;
    for(int i = 1; i <= p; i ++) {
        int u, v, w;
        cin>>u>>v>>w;
        G[u].push_back(P(v,w));
        G[v].push_back(P(u,w));
    }
    spfa(s);
    int ans = 10000010;
    for(int i = 0; i <= k; i ++) ans = min(ans,d[t][i]);
    printf("%d\n",ans);
    return 0;
}

做法二:二分答案+双端队列BFS

对于边权只有0和1的最短路问题,可以使用双端队列BFS求解,复杂度O((N+P)\log MAX\_L)

#include <algorithm>
#include <cstdio>
#include <cstring>
#include <deque>
#include <vector>
using namespace std;
#define maxv 1005
struct edge
{
    int v, w;
    edge(int a=0,int b=0){v=a,w=b;}
};
int V, E, K, d[maxv];
bool used[maxv];
vector<edge> G[maxv];

int bfs(int m)
{
    memset(d, 0x3f, sizeof(d));
    memset(used, 0, sizeof(used));
    deque<int> q;
    d[1] = 0;
    q.push_back(1);
    while (!q.empty())
    {
        int u = q.front();
        q.pop_front();
        if (used[u])
            continue;
        used[u] = 1;
        for (int i = 0; i < (int)G[u].size(); ++i)
        {
            edge &e = G[u][i];
            if (e.w > m)
            {
                if (d[e.v] > d[u] + 1)
                    d[e.v] = d[u] + 1, q.push_back(e.v);
            }
            else
            {
                if (d[e.v] > d[u])
                    d[e.v] = d[u], q.push_front(e.v);
            }
        }
    }
    return d[V];
}

int main()
{
    scanf("%d%d%d", &V, &E, &K);
    int maxl = 0;
    for (int i = 0; i < E; ++i)
    {
        int u, v, w;
        scanf("%d%d%d", &u, &v, &w);
        maxl = max(maxl, w);
        G[v].push_back(edge(u, w));
        G[u].push_back(edge(v, w));
    }
    if (bfs(maxl))
    {
        puts("-1");
        return 0;
    }
    int lb = -1, ub = maxl;
    while (ub - lb > 1)
    {
        int mid = (lb + ub) >> 1;
        if (bfs(mid) <= K)
            ub = mid;
        else
            lb = mid;
    }
    printf("%d\n", ub);
    return 0;
}

做法三:二分答案+dijkstra

对于二分的判定,dijkstra求最短路,复杂度O(P\log N\log MAX\_L)

5. EX CH6101 / P1073 最优贸易

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 6, M = 1e6 + 6;
int n, m, tot, Price[N], Ans;
int Head[N], Side[M], Next[M], ans[N];
int fHead[N], fSide[M], fNext[M], fans[N];
priority_queue<pair<int, int> > q;
priority_queue<pair<int, int> > fq;
 
int main() {
	cin >> n >> m;
	for (int i = 1; i <= n; i++) scanf("%d", &Price[i]);
	for (int i = 1; i <= m; i++) {
		int x, y, z;
		scanf("%d %d %d", &x, &y, &z);
		Side[++tot] = y;
		Next[tot] = Head[x];
		Head[x] = tot;
		fSide[tot] = x;
		fNext[tot] = fHead[y];
		fHead[y] = tot;
		if (z == 2) {
			Side[++tot] = x;
			Next[tot] = Head[y];
			Head[y] = tot;
			fSide[tot] = y;
			fNext[tot] = fHead[x];
			fHead[x] = tot;
		}
	}
	memset(ans, 0x3f, sizeof(ans));
	memset(fans, 0xcf, sizeof(fans));
	ans[1] = Price[1];
	fans[n] = Price[n];
	q.push(make_pair(-ans[1], 1));
	fq.push(make_pair(fans[n], n));
	while (q.size()) {
		int x = q.top().second;
		q.pop();
		for (int i = Head[x]; i; i = Next[i]) {
			int y = Side[i];
			if (ans[y] > ans[x]) {
				ans[y] = ans[x];
				ans[y] = min(ans[y], Price[y]);
				q.push(make_pair(-ans[y], y));
			}
		}
	}
	while (fq.size()) {
		int x = fq.top().second;
		fq.pop();
		for (int i = fHead[x]; i; i = fNext[i]) {
			int y = fSide[i];
			if (fans[y] < fans[x]) {
				fans[y] = fans[x];
				fans[y] = max(fans[y], Price[y]);
				fq.push(make_pair(fans[y], y));
			}
		}
	}
	for (int i = 1; i <= n; i++) Ans = max(Ans, fans[i]-ans[i]);
	cout << Ans << endl;
	return 0;
}

6. EX BZOJ2200 [Usaco2011 Jan] 道路和航线

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
using namespace std;
typedef long long ll;
const int N=3e5+10,M=3e5+10;
const int INF=0x7f7f7f7f;
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 x,y,c,next;
}a[N]; int len,last[N];
void ins(int x,int y,int c)
{
    a[++len].x=x;a[len].y=y;a[len].c=c;
    a[len].next=last[x];last[x]=len;
}

bool v[N];
int cnt;
vector<int> has[N];
int bl[N];
void dfs(int x)
{
    v[x]=1;
    has[cnt].push_back(x);
    for(int k=last[x];k;k=a[k].next)
    {
        int y=a[k].y;
        if(!v[y])bl[y]=bl[x],dfs(y);
    }
}

struct node
{
    int d,id;
    bool operator <(const node &b) const
    {
        return d>b.d;
    }
};

priority_queue<pair<int,int> > q;
int deg[N];
int n,R,P,st;
int list[M],head,tail;
int dis[N];
int main()
{
//	freopen("a.in","r",stdin);
//	freopen("a.out","w",stdout);
    n=read(); R=read(); P=read(); st=read();
    len=0; memset(last,0,sizeof(last));
    for(int i=1;i<=R;i++)
    {
        int x=read(),y=read(),c=read();
        ins(x,y,c);
        ins(y,x,c);
    }
//get scc
    cnt=0; memset(bl,0,sizeof(bl));
    memset(v,0,sizeof(v));
    for(int i=1;i<=n;i++)
        if(!v[i])
        {
            cnt++; bl[i]=cnt;
            dfs(i);
        }
//rebuild to DAG
    memset(deg,0,sizeof(deg));
    for(int i=1;i<=P;i++)
    {
        int x=read(),y=read(),c=read();
        ins(x,y,c); ins(bl[x]+n,bl[y]+n,c);
    }
//Get deg
    head=1,tail=1;
    list[1]=bl[st]+n;
    memset(v,0,sizeof(v));
    while(head<=tail)
    {
        int x=list[head];
        for(int k=last[x];k;k=a[k].next)
        {
            int y=a[k].y;
            if(!v[y])v[y]=1,list[++tail]=y;
            deg[y-n]++;
        }
        head++;
    }
//Dij+Topsort
    memset(dis,0x7f,sizeof(dis)); dis[st]=0;
    memset(v,0,sizeof(v));
    head=1,tail=1;
    list[1]=bl[st];
    while(head<=tail)
    {
        int cc=list[head];
//        for(int i=1;i<=n;i++)if(bl[i]==cc)q.push(make_pair(-dis[i],i));
        int siz=has[cc].size();
        for(int i=0;i<siz;i++) q.push(make_pair(-dis[has[cc][i]],has[cc][i]));
        while(q.size())
        {
            int x=q.top().second;q.pop();
            if(v[x])continue;
            v[x]=1;
            for(int k=last[x];k;k=a[k].next)
            {
                int y=a[k].y;
                if(bl[x]!=bl[y]){deg[bl[y]]--;
                        if(deg[bl[y]]==0)list[++tail]=bl[y];}
                if(dis[y]>dis[x]+a[k].c)
                {
                    dis[y]=dis[x]+a[k].c;
                    if(bl[x]==bl[y])q.push(make_pair(-dis[y],y));
                }
            }
        }
        
        head++;
    }

    for(int i=1;i<=n;i++)
    {
        if(dis[i]==INF) puts("NO PATH");
        else printf("%d\n",dis[i]);
    }
    return 0;
}

7. 任意两点间最短路径

Floyd算法 O(n^3)

#include<bits/stdc++.h>
using namespace std;
const int MAXN=105;
const int INF=0x3f3f3f3f;
int graph[MAXN][MAXN];//邻接矩阵存图
int n,m;//n个点,m个边
void floyd(){
    int s=1; //定义起点
    for(int k=1;k<=n;k++)
        for(int i=1;i<=n;i++)
            if(graph[i][k]!=INF)
            for(int j=1;j<=n;j++)
                if(graph[i][j]>graph[i][k]+graph[k][j])
                    graph[i][j]=graph[i][k]+graph[k][j];
    cout<<graph[s][n]<<endl;
}
int main(){
    while(~scanf("%d%d",&n,&m)&&n&&m){
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                graph[i][j]=INF;//任意两点间初始距离为无穷大
        while(m--){
            int a,b,c;
            cin>>a>>b>>c;
            graph[a][b]=graph[b][a]=c;//邻接矩阵存图
        }
        floyd();
    }
    return 0;
}

传递闭包

//传递闭包 floyd算法
#include<bits/stdc++.h>
using namespace std;
const int MAXN=105;
const int INF=0x3f3f3f3f;
int graph[MAXN][MAXN];//邻接矩阵存图
int n,m;//n个点,m个边
void floyd(){
    int s=1; //定义起点
    for(int k=1;k<=n;k++)
        for(int i=1;i<=n;i++)
            if(graph[i][k]!=INF)
            for(int j=1;j<=n;j++)
                graph[i][j] |= graph[i][k] & graph[k][j];
    cout<<graph[s][n]<<endl;
}
int main(){
    while(~scanf("%d%d",&n,&m)&&n&&m){
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                graph[i][j]=INF;//任意两点间初始距离为无穷大
        while(m--){
            int a,b,c;
            cin>>a>>b>>c;
            graph[a][b]=graph[b][a]=c;//邻接矩阵存图
        }
        floyd();
    }
    return 0;
}

8. EX POJ1094 Sorting It All Out

9. EX POJ1734 Sightseeing trip

10. POJ3613 EX Cow Relays

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值