首页 > 其他分享 >图论

图论

时间:2023-05-24 13:11:58浏览次数:30  
标签:图论 dist idx int st que now

///朴素dijkstra算法 —— 模板题 AcWing 849. Dijkstra求最短路 I
///时间复杂是 O(n2+m)O(n2+m), nn 表示点数,mm 表示边数
#include<bits/stdc++.h>
using namespace std;
const int N=510;
int n,m;
int g[N][N];  // 存储每条边
int dist[N];  // 存储1号点到每个点的最短距离
bool st[N];   // 存储每个点的最短路是否已经确定

// 求1号点到n号点的最短路,如果不存在则返回-1
int dijkstra()
{
    memset(dist, 0x3f, sizeof dist);//将每个点每个距离赋值为无穷大
    dist[1] = 0;

    for (int i = 0; i < n - 1; i ++ )
    {
        int t = -1;     // 在还未确定最短路的点中,寻找距离最小的点
        for (int j = 1; j <= n; j ++ )
            if (!st[j] && (t == -1 || dist[t] > dist[j]))
                t = j;

        // 用t更新其他点的距离
        for (int j = 1; j <= n; j ++ )
            dist[j] = min(dist[j], dist[t] + g[t][j]);

        st[t] = true;
    }

    if (dist[n] == 0x3f3f3f3f) return -1;
    return dist[n];
}
int main()
{
    memset(g,0x3f,sizeof g);
    cin>>n>>m;
    while(m--)
    {
        int a,b,c;
        cin>>a>>b>>c;
        g[a][b]=min(g[a][b],c);
    }
    int t=dijkstra();
    cout<<t<<endl;
}
///堆优化版dijkstra —— 模板题 AcWing 850. Dijkstra求最短路 II
///时间复杂度 O(mlogn)O(mlogn), nn 表示点数,mm 表示边数
#include<bits/stdc++.h>
using namespace std;
const int N=10010;
int h[N],e[N],ne[N],idx,n,m,w[N];
int dist[N];
bool st[N];
typedef pair<int ,int>PII;
void add(int a,int b,int c)
{
    e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;/// 邻接表存储所有边
}
int dijkstra()
{
    memset(dist,0x3f,sizeof dist);
    dist[1]=0;
    priority_queue<PII,vector<PII>,greater<PII>> heap;//创立小根堆
    heap.push({0,1});/// first存储距离,second存储节点编号
    while(!heap.empty())
    {
        auto t=heap.top();
        heap.pop();
        int ver=t.second,dis=t.first;
        if(st[ver]) continue;
        for(int i=h[ver];i!=-1;i=ne[i])
        {
            int j=e[i];
            if(dist[j]>dis+w[i])
            {
                dist[j]=dis+w[i];
                heap.push({dist[j],j});
            }
        }
    }
        if (dist[n] == 0x3f3f3f3f) return -1;
    return dist[n];
}
int main()
{
    memset(h,-1,sizeof h);
    cin>>n>>m;
    while(m--)
    {
        int a,b,c;
        cin>>a>>b>>c;
        add(a,b,c);
    }
    int t=dijkstra();
    cout<<t<<endl;
}
///最短路计数,使用邻接表动态数组,bfs
#include<bits/stdc++.h>
using namespace std;
const int N=1000010,mod=100003;
vector<int> g[N];
int dist[N];
bool vis[N];
int cnt[N];
int n,m;
void bfs()
{
    memset(dist,0x3f3f3f,sizeof dist);
    memset(vis,false,sizeof vis);
    queue<int>que;
    dist[1]=0;
    cnt[1]=1;
    que.push(1);
    while(!que.empty())
    {
        int u=que.front();
        que.pop();
        if(vis[u]) continue;
        vis[u]=true;
        for(auto v:g[u])
        {
            if(!vis[v])
            {
                if(dist[v]>dist[u]+1)///如果此时不是1最短路,更新最短路
                {
                    dist[v]=dist[u]+1;
                    cnt[v]=cnt[u];
                    cnt[v]%=mod;
                    que.push(v);
                }
                else if(dist[v]==dist[u]+1)///如果走的路是最短路,就在这个地方加上1;
                {
                    cnt[v]+=cnt[u];
                    cnt[v]%=mod;
                }
            }
        }
    }
}
int main()
{
    cin>>n>>m;
    for(int i=0;i<m;i++)
    {
        int a,b;
        cin>>a>>b;
        g[a].push_back(b);///储存邻接表
        g[b].push_back(a);
    }
    bfs();
    for(int i=1;i<=n;i++)
        cout<<cnt[i]<<endl;
    return 0;
}
///ballman-ford
///存在负边时使用
#include<bits/stdc++.h>
using namespace std;
const int N=10010;
int n,m,k;
int dist[N],backup[N];
struct node
{
    int a,b,w;
}edge[N];
int ballman_ford()
{
    memset(dist,0x3f,sizeof dist);
    dist[1]=0;
    for(int i=0;i<k;i++)
    {
        memcpy(backup,dist,sizeof dist);
        for(int j=0;j<m;j++)
        {
            int a=edge[j].a,b=edge[j].b,w=edge[j].w;
            dist[b]=min(dist[b],backup[a]+w);
        }
    }
    if(dist[n]>0x3f3f/2) return -1;
    else return dist[n];
}
int main()
{
    cin>>n>>m>>k;
    for(int i=0;i<m;i++)
    {
        int a,b,w;
        cin>>a>>b>>w;
        edge[i]={a,b,w};
    }
    int t =ballman_ford();
    if(t==-1)  puts("impossible");
    else cout<<t<<endl;
    return 0;
}
///SPFA路径
///使用的邻接表储存
#include<bits/stdc++.h>
using namespace std;
const int N=10010;
int n,m;
int h[N],e[N],ne[N],w[N],idx;
int dist[N];
bool st[N];
void add(int a,int b,int c)
{
    e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
int spfa()
{
    memset(dist,0x3f,sizeof dist);
    dist[1]=0;
    queue<int>que;
    que.push(1);
    st[1]=true;
    while(!que.empty())
    {
        int now=que.front();
        que.pop();
        st[now]=false;
        for(int i=h[now];i!=-1;i=ne[i])
        {
            int j=e[i];
            if(dist[j]>dist[now]+w[i])
            {
                dist[j]=dist[now]+w[i];
                if(!st[j])
                {
                    que.push(j);
                    st[j]=true;
                }
            }
        }
    }
    if(dist[n]>0x3f3f/2) return -1;
    else return dist[n];
}
int main()
{
    cin>>n>>m;
    memset(h,-1,sizeof h);
    while(m--)
    {
        int a,b,c;
        cin>>a>>b>>c;
        add(a,b,c);
    }
    int t=spfa();
    if(t==-1) cout<<"impossible"<<endl;
    else cout<<t<<endl;
    return 0;
}
///更加详细的spfa
#include <bits/stdc++.h>
using namespace std;

const int N = 2030, M = N * 50;        //N为顶点数,M为边数

//数组构造邻接表, n为定点数,m为边数
int n, m
int h[N], e[M], w[M], ne[M], idx;
int dist[N];
queue<int> q;
bool st[N];

// 添加一条边a->b,边权为c
void add(int a, int b, int c)
{
    //e[idx]指的是编号为idx的边的去向为何处,h[a]存储的是从a出发的所有边的单链表表头
    e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
}

void spfa()  // 求1号点到n号点的最短路距离
{
    memset(dist, 0x3f, sizeof dist);    //0x3f代表最大值,最小值可用0xcf
    dist[1] = 0;
    q.push(1);
    st[1] = true;

    while (q.size())        //while循环控制出队
    {
        int t = q.front();
        q.pop();
        st[t] = false;

        for (int i = h[t]; i != -1; i = ne[i])        //for循环控制入队+更新
        {
            int j = e[i];
            if (dist[j] > dist[t] + w[i])
            {
                dist[j] = dist[t] + w[i];
            //  cnt[j] = cnt[t] + 1;             // 用于判断是否存在负环
            //  pre[i] = t;                         // 用于记录前驱节点(记录路径)
                if (!st[j])     // 如果队列中已存在j,则不需要将j重复插入
                {
                    q.push(j);
                    st[j] = true;
                }
            //  if(cnt[j] >= n) return true;    // 用于判断是否存在负环
            }

            // 最短路计数要区分dist[j] > 和 == 的情况,不能只讨论dist[j] > dist[t] + w[i]
            /**
            if (dist[j] > dist[t] + w[i]) {
                dist[j] = dist[t] + w[i];
                cnt[j] = cnt[t]
            }
            else if (dist[j] == dist[t] + w[i]) {
                cnt[i] += cnt[t];    //最短路计数
            }
            if (!st[j])     // 如果队列中已存在j,则不需要将j重复插入
            {
                q.push(j);
                st[j] = true;
            }
            */
        }
    }
}

int main(){
    memset(h, -1, sizeof h);
    ...            //需先通过add函数,采取邻接表的方式构造好图
    spfa();
    ...         //输出路径权值和、判断是否有负环、输出最短路径...等操作

    /** 记录路径操作 */
    stack<int> help;    //遍历前驱节点,压入栈中,再取出来时可以变为正序的
    for (int i = n; ~i; i=pre[i]) help.push(i);    // n为路径终点
    while (help.size()) {
        printf("%d ", help.top());
        help.pop();
    }
}

///二维数组存SPFA
#include<bits/stdc++.h>
using namespace std;
const int N=10010;
struct node
{
    int b,w;
}e;
int n,m;
int dist[N];
bool st[N];
vector<node>g[N];
int spfa()
{
    memset(dist,0x3f3f,sizeof dist);
    dist[1]=0;
    queue<int>que;
    que.push(1);
    st[1]=true;///表示我们的这个点已经入队列了
    while(!que.empty())
    {
        int now=que.front();
        que.pop();
        st[now]=false;///队头出去了,那就是这个点不在这个队列里了标记false;
       for(int i=0;i<g[now].size();i++)
       {
           int v=g[now][i].b;
           if(dist[v]>dist[now]+g[now][i].w)
           {
               dist[v]=dist[now]+g[now][i].w;
               if(!st[v])///如果队列里面没有他,再加入队列;
               {
                   st[v]=true;
                   que.push(v);
               }
           }
       }
    }
    if(dist[n]>0x3f3f/2) return -1;
    else return dist[n];
}
int main()
{
    cin>>n>>m;
    for(int i=0;i<m;i++)
    {
        int a;
        cin>>a>>e.b>>e.w;
        g[a].push_back(e);///存入数据
    }
    int t=spfa();
    if(t==-1) cout<<"impossible"<<endl;
    else cout<<t<<endl;
    return 0;
}
///SPFA判断负环
#include<bits/stdc++.h>
using namespace std;
const int N=10010;
struct node
{
    int b,w;
}e;
int n,m;
int dist[N],cnt[N];
bool st[N];
vector<node>g[N];
bool spfa()
{
    queue<int>que;
    for(int i=1;i<=n;i++)///有可能从起点1,走不到负环的位置,所以1只需要把所有点都加入队列遍历即可
    {
        st[i]=true;
        que.push(i);
    }
    while(!que.empty())
    {
        int now=que.front();
        que.pop();
        st[now]=false;///队头出去了,那就是这个点不在这个队列里了标记false;
       for(int i=0;i<g[now].size();i++)
       {
           int v=g[now][i].b;
           if(dist[v]>dist[now]+g[now][i].w)
           {
               dist[v]=dist[now]+g[now][i].w;
               cnt[v]=cnt[now]+1;///这个代表的是当前这条路的边数
               if(cnt[v]>=n) return true;///如果,我是说如果当前路的边数已经大于n了,那很明显存在负环;
               if(!st[v])///如果队列里面没有他,再加入队列;
               {
                   st[v]=true;
                   que.push(v);
               }
           }
       }
    }
    return false;
}
int main()
{
    cin>>n>>m;
    for(int i=0;i<m;i++)
    {
        int a;
        cin>>a>>e.b>>e.w;
        g[a].push_back(e);///存入数据
    }
    if(spfa()) cout<<"YES"<<endl;
    else cout<<"NO"<<endl;
    return 0;
}
///floyd算法,适用于多个询问,复杂度0n3
#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int n,m,k;
int d[N][N];
void floyd()
{
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            for(int x=1;x<=n;x++)
            d[j][x]=min(d[j][x],d[j][i]+d[i][x]);
}
int main()
{
    cin>>n>>m>>k;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
        if(i==j) d[i][j]=0;
    else d[i][j]=1e9;
    while(m--)
    {
        int a,b,w;
        cin>>a>>b>>w;
        d[a][b]=min(d[a][b],w);
    }
    floyd();
    while(k--)
    {
        int a,b;
        cin>>a>>b;
        if(d[a][b]>1e9/2) cout<<"impossible"<<endl;
        else cout<<d[a][b]<<endl;
    }
    return 0;
}
///分层图
//https://blog.csdn.net/qq_45735851/article/details/108219481?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522168420864216800222869973%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=168420864216800222869973&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_positive~default-1-108219481-null-null.142^v87^control,239^v2^insert_chatgpt&utm_term=%E5%88%86%E5%B1%82%E5%9B%BE%E6%9C%80%E7%9F%AD%E8%B7%AF&spm=1018.2226.3001.4187
///分层图最经典的例题之一
///可以试想一下,咱们最短路是一个图,分层图的意思是创建多个图,然后每个图都不一样,遍历完之后找到最优解
///可以看出来分层图难在建图
#include<bits/stdc++.h>
using namespace std;
const int N=10000010;
int n,m,s,k,t,idx;
int e[N],ne[N],h[N],dist[N],w[N];
bool vis[N];
typedef pair<int,int>pii;
void add(int a,int b,int c)
{
    e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
void dijkstra()///标准模板
{
    priority_queue<pii,vector<pii>,greater<pii>> que;
    memset(dist,0x3f3f3f,sizeof dist);
    dist[s]=0;
    que.push({0,s});
    while(!que.empty())
    {
        auto now=que.top();
        que.pop();
        int y=now.first,x=now.second;
        for(int i=h[x];i!=-1;i=ne[i])
        {
            int j=e[i];
            if(dist[j]>y+w[i])
            {
                dist[j]=y+w[i];
                que.push({dist[j],j});
            }
        }
    }
}
int main()
{
    memset(h,-1,sizeof h);
    cin>>n>>m>>k>>s>>t;
    for(int i=0;i<m;i++)
    {
        int a,b,c;
        cin>>a>>b>>c;
        add(a,b,c);
        //add(b,a,c);
        for(int j=1;j<=k;j++)///建图
        {
            add(j*n+a,j*n+b,c);///把一层建好
            //add(j*n+b,j*n+a,c);
            add((j-1)*n+a,j*n+b,0);///把上一层建好,代表从上到下走0的距离;
           // add((j-1)*n+b,j*n+a,0);
        }
    }
    for(int i=0;i<k;i++) add(i*n+t,(i+1)*n+t,0);///有可能答案不需要非要用完k次免费次数,所以这里把所有终点都用距离为0连起来;
    dijkstra();
    int minn=0x3f;
    for(int i=0;i<=k;i++) minn=min(minn,dist[i*n+t]);///遍历所有终点,有可能没必要用完k次;
    cout<<minn;
    return 0;
}
///floyd算法的应用
//https://www.luogu.com.cn/record/110497608
///floyd算法应用与求每个点到任意一点的最短距离,然后根据此算法就可以按照题目要求的路径算出最短路径来
///不需要建图,三重暴力循环解决战斗
#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int n,m,num[100005],d[N][N],res;
int main()
{
    cin>>n>>m;
    for(int i=1;i<=m;i++)
    {
        cin>>num[i];
    }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            cin>>d[i][j];
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            for(int k=1;k<=n;k++)
                d[j][k]=min(d[j][k],d[j][i]+d[i][k]);
    int st=1,ed=n;
    for(int i=1;i<=m;i++)
    {
        res+=d[st][num[i]];
        st=num[i];
    }
    res+=d[st][ed];
    cout<<res<<endl;
    return 0;
}
///floyd 应用2 变形
//https://www.luogu.com.cn/problem/B3611
#include<bits/stdc++.h>
using namespace std;
const int N=500;
int n,d[N][N],mp[N][N];
int main()
{
    cin>>n;;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++) cin>>d[i][j];
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            for(int k=1;k<=n;k++)
                d[j][k]=max(d[j][k],min(d[j][i],d[i][k]));///1表示为连通,0表示为不连通,试想一下,如果d[j][k]和另一个有一个为1,那是不是就是可以连通?
                ///但是这里为什么第二个要取min呢,如果这两者有一个为0,说明,j和k之间没有中转站,此时如果j和k不是直接连的也就是,d[j][k]不是1,那么这两点不可能连上;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
            cout<<d[i][j]<<" ";
        cout<<endl;
    }
    return 0;
}

 

标签:图论,dist,idx,int,st,que,now
From: https://www.cnblogs.com/o-Sakurajimamai-o/p/17428001.html

相关文章

  • maximum clique 1 (牛客多校) (转化为图论, 二分图的最大独立集)
    题面大意:给出N个数, 找出最大的子集size使得子集中,任意2个数的二进制下,每一位,至少有2位不同 思路:N只有5000,可以直接暴力建边,转化位图论2种建边方式:一种是能在一个集合的2个数,建一条边,(没有办法去处理这个问题)一种是不能在一个集合的就建一条......
  • 2022.11.23 51nod 图论专场?
    A反转Dag图:题面给出一个\(n\)个点\(m\)条边的有向图,顶点编号\(1\)到\(n\),边的编号为\(1\)到\(m\)。你可以选择一些边进行反转(即从\(u\)到\(v\)的边反转后变为从\(v\)到\(u\)的边),将每条边反转都需要一定代价。最终使得整个图变成一个\(Dag\)图。总的反......
  • 图论专题 1
    喜报:图论专题1前五题都是uoj题。后边是WC2007剪刀石头布和两个*3100。喜报:我不会图论!观察了一下发现除了我们以外做这些题的确实都不是活人。那我们在和波特对战!很恐怖!随手交了一发新年的追逐战居然最优解了。该说uoj卡多项式全家桶最优解的没多少吗。速度明显慢下来......
  • 图论 plus
    P5304「GXOI/GZOI2019」旅行者如果是无向边,那么以所有感兴趣的城市为起点跑一遍Dijkstra即可,遇到访问过的点就说明找到了最短路。但题目已经写清楚可能存在自环和重边,而起点和终点相同不算两两之间,所以还需要额外记录一下起点。变成有向边后,我们不能在任意一个点处合并最短......
  • 学习笔记:矩阵快速幂与图论
    1计算路径数对于一个边权为\(\bf1\)的图(有向或无向)的邻接矩阵\(G\),考虑它的幂的意义是什么。设\(c_{i,j}\)表示\(i\)和\(j\)之间是否有连边,则有\[G=\begin{bmatrix}c_{1,1}&c_{1,2}&\cdots&c_{1,n}\\c_{2,1}&c_{2,2}&\cdots&c_{2,n}\\\vdots&am......
  • Codeforces [Hello 2020] 1284F New Year and Social Network(图论匹配推理+lct)
    https://codeforces.com/contest/1284/problem/F题目大意:有两个大小为n的树T1和T2.T2中的每条边(u,v)可以匹配T1中u到v路径上的所有边。求最大匹配,并给出方案。\(1<=n<=250000\)题解:首先你需要观察样例大胆猜想一定有完美匹配。考虑T1中的一个叶子x和它的父亲y。显然的是,从T2中随......
  • Fine-Grained学习笔记(4):条件下界与归约,图论问题的复杂度归约理论
    和P与NP问题一样,Fine-Grained领域中的许多问题也能相互归约,这意味着当这些问题中的任意一个问题的复杂度下界得到了证明或证伪,那么一系列问题的复杂度下界就都能够得到解决.APSP猜想:不存在$O(|V|^{3-\delta})$时间的(对于任意实数边权图都有效的)(确定性的)APSP算法.APSP猜......
  • 搜索与图论(1)
    DFS深度优先遍历回溯、剪枝、对应一条搜索树 全排列问题#include<iostream>#include<algorithm>usingnamespacestd;constintN=10;intn;intpath[N];//存方案的boolst[N];//true表示当前数组已被用过voiddfs(intu){if(u==n){......
  • 线性代数与图论小记
    觉得很有意思……开始不务正业。行列式定义\[|A|=\sum_p(-1)^{\text{inv}(p)}\prod_{i=1}^na_{i,p_i}\]很基本也很重要,感性理解就是通过类似容斥的方式计算了一个\(n\)维体的体积或者说缩放率?如果\(A\)中有若干条行向量/列向量线性相关体积自然是\(0\)。或者说可以用......
  • 图论之存图
    图论之存图2023.4.25概述主要有四种每种都有不同的用途为方便,下文记点集为\(|V|\),大小为\(n\);边集为\(|E|\),大小为\(m\)。01直接存边时间复杂度:遍历(DFS,BFS)\(\Theta(\infin)\)判断是否存在\(\Theta(m)\)空间复杂度:\(\Theta(m)\)代码structEdge{......