第三章 图论【未完成】

目录

单源最短路的建图方式

1129. 热浪【最普通的最短路板子】

在这里插入图片描述
https://www.acwing.com/problem/content/1131/
详细解析
就常规的最短路,其它的没啥的特别的东西。

#include<bits/stdc++.h>
using namespace std;
const int N=1e5*2+10;
typedef pair<int,int> PII;
int h[N],e[N],w[N],ne[N],idx;
int n,m,startx,endx,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++;
}
void Dijkstra()
{
    memset(dist,0x3f,sizeof dist);
    dist[startx]=0;
    priority_queue<PII,vector<PII>,greater<PII>> heap; heap.push({0,startx});
    while(heap.size())
    {
        auto t=heap.top(); heap.pop();
        int u=t.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];
                heap.push({dist[j],j});
            }
        }
    }
}
int main(void)
{
    memset(h,-1,sizeof h);
    cin>>n>>m>>startx>>endx;
    for(int i=0;i<m;i++)
    {
        int a,b,c; cin>>a>>b>>c;
        add(a,b,c),add(b,a,c);
    }
    Dijkstra();
    cout<<dist[endx];
    return 0;
}

1128. 信使【基本的最短路 + 稍微的扩展】

在这里插入图片描述
https://www.acwing.com/problem/content/description/1130/
数据范围很小,故直接用floyd算法。题目要求的就是各个点距离源点的最远的那个距离。
故直接跑一边,然后枚举每一个点到源点的距离求一个最大的即可。

#include<bits/stdc++.h>
using namespace std;
const int N=110;
int dist[N][N],n,m;
int main(void)
{
    cin>>n>>m;
    memset(dist,0x3f,sizeof dist);
    for(int i=1;i<=n;i++) dist[i][i]=0;
    while(m--)
    {
        int a,b,c; cin>>a>>b>>c;
        dist[a][b]=dist[b][a]=min(dist[a][b],c);
    }
    for(int k=1;k<=n;k++)
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                dist[i][j]=min(dist[i][j],dist[i][k]+dist[k][j]);
    int ans=0,flag=0;
    for(int i=2;i<=n;i++) 
        if(dist[1][i]!=0x3f3f3f3f) ans=max(ans,dist[1][i]);
        else flag=1;
    if(!flag) cout<<ans<<endl;
    else puts("-1");
    return 0;
}

1127. 香甜的黄油【稍微变种的最短路】

在这里插入图片描述
https://www.acwing.com/problem/content/1129/
就枚举每一个牧场,求各个点到该点的和,在各个的和中求一个最小的和即可。

#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
typedef long long int LL;
const int N=810;
int n,m,t;
int h[N],e[N*10],w[N*10],ne[N*10],idx;
int dist[N],cow[N],st[N];
LL ans=1e9;
void add(int a,int b,int c)
{
    e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
void Dijkstra(int startx)
{
    memset(dist,0x3f,sizeof dist);
    memset(st,0,sizeof st);
    dist[startx]=0;
    priority_queue<PII,vector<PII>,greater<PII>>heap; heap.push({0,startx});
    while(heap.size())
    {
        auto t=heap.top(); heap.pop();
        int u=t.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];
                heap.push({dist[j],j});
            }
        }
    }
    LL sum=0;
    for(int i=0;i<n;i++) sum+=dist[cow[i]];
    ans=min(ans,sum);
}
int main(void)
{
    memset(h,-1,sizeof h);
    cin>>n>>m>>t;
    for(int i=0;i<n;i++) cin>>cow[i];
    for(int i=0;i<t;i++)
    {
        int a,b,c; cin>>a>>b>>c;
        add(a,b,c),add(b,a,c);
    }
    for(int i=1;i<=m;i++) Dijkstra(i);//枚举每一个牧场
    cout<<ans<<endl;
    return 0;
}

1126. 最小花费【Dijkstra 求最大乘积】

在这里插入图片描述
https://www.acwing.com/problem/content/1128/
详细题解
这里我们要求一个最大的乘积,才可以让花费的钱越小。因为这里的边权都是(0,1)之间的故乘以一个数必定变小
有单调性,故可以用Dijkstra来求解。

关于Dijkstra
考虑Dij为什么不行,因为Dij的最短路算法是贪心的,它贪心的选最小的并且认为这个点不会被更新了,为什么不会被更新了?因为没有负权边,从而你加入优先队列里的dis只会越来越大,毕竟一个数+一个正数一定变大,不可能以后取出来某一个点更新这个点,故我这个点取出来了,就不会再使用。Dij的正确性是基于 “放入优先队列的点的dis是单调的” 这一条件。
所以 对于求最短路,我们要求dis单调递增,即不存在负权边,对于最大乘积,我们要求乘积只允许单调变小,即乘的在0,1之间,对于最小乘积,我们要求乘积必须单调变大,即所有边权都是>=1的.

#include<bits/stdc++.h>
using namespace std;
const int N=1e5*2+10;
int h[N],e[N],ne[N],n,m,idx;
int st[N],A,B;
double w[N],dist[N];
void add(int a,int b,double c)
{
    e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
void Dijkstra(int A)
{
    dist[A]=1.0;
    priority_queue<pair<double,int>>heap; heap.push({1.0,A});
    while(heap.size())
    {
        auto t=heap.top(); heap.pop();
        int u=t.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];
                heap.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; cin>>a>>b>>c;
        add(a,b,(100-c)/100.0);
        add(b,a,(100-c)/100.0);
    }
    cin>>A>>B;
    Dijkstra(A);
    printf("%.8lf",100.0/dist[B]);
    return 0;
}

920. 最优乘车【根据题意建图】

在这里插入图片描述
https://www.acwing.com/problem/content/922/
根据每一条线,建边,注意是有向边。求最短路,最短路的结果是最少的坐车次数,减1即为最少的换乘次数。

#include<bits/stdc++.h>
using namespace std;
const int N=510;
int dist[N],g[N][N],st[N],n,m;
string s,a;
void Dijkstra()
{
    memset(dist,0x3f,sizeof dist);
    dist[1]=0;
    for(int i=0;i<n;i++)
    {
        int t=-1;
        for(int j=1;j<=n;j++)  if(!st[j]&&(t==-1 || dist[j]<dist[t])) t=j;
        st[t]=1;
        for(int j=1;j<=n;j++) dist[j]=min(dist[j],dist[t]+g[t][j]);
    }
}
int main(void)
{
    memset(g,0x3f,sizeof g);
    cin>>m>>n;
    for(int i=1;i<=n;i++) g[i][i]=0;
    getchar();
    for(int i=0;i<m;i++)
    {
        getline(cin,s);
        stringstream l(s);
        vector<int>ve;
        while(l>>a) ve.push_back(stoi(a));
        for(int i=0;i<ve.size();i++)
        {
            for(int j=i+1;j<ve.size();j++)
            {
                int x=ve[i],y=ve[j];
                g[x][y]=1;
            }
        }
    }
    Dijkstra();
    if(dist[n]==0x3f3f3f3f) puts("NO");
    else cout<<dist[n]-1;
    return 0;
}

903. 昂贵的聘礼【建立虚拟结点 通过虚拟结点建图】

在这里插入图片描述
https://www.acwing.com/problem/content/905/
建立一个虚拟结点,通过虚拟结点和其它的各个结点建图。
最后枚举可以买到1号点的所有区间。再所有的答案中去一个最小的即可

#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int g[N][N],dist[N],st[N],n,m;
int level[N],ans=1e9;
void Dijkstra(int l,int r)
{
    memset(dist,0x3f,sizeof dist); dist[0]=0;
    memset(st,0,sizeof st);
    for(int i=0;i<n;i++)
    {
        int t=-1;
        for(int j=0;j<=n;j++) if(!st[j]&&(t==-1 || dist[j]<dist[t])) t=j;
        st[t]=1;
        for(int j=0;j<=n;j++)
        {
            if(level[j]>=l&&level[j]<=r) dist[j]=min(dist[j],dist[t]+g[t][j]);
        }
    }
    ans=min(ans,dist[1]);
}
int main(void)
{
    memset(g,0x3f,sizeof g);
    cin>>m>>n;
    for(int i=1;i<=n;i++)
    {
        int w,t; cin>>w>>level[i]>>t;
        g[0][i]=min(g[0][i],w);
        for(int j=0;j<t;j++)
        {
            int id,price; cin>>id>>price;
            g[id][i]=min(g[id][i],price);
        }
    }
    for(int i=level[1]-m;i<=level[1];i++) Dijkstra(i,i+m);
    cout<<ans;
    return 0;
}

单源最短路的综合应用

1135. 新年好【最短路 + 排列组合】

在这里插入图片描述
https://www.acwing.com/problem/content/description/1137/
很容易的想到,枚举5!的全排列,每次Dijkstra()一遍。
这样会超时。我们可以先预处理出,每一个点到其它点的最短路。然后再爆搜,这样的话Dijkstart()只需6此即可。
大大的提升了性能。

#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
const int N=1e5*2+10;
int h[N],e[N],w[N],ne[N],idx;
int st[N],dist[N];
int n,m,a[10];
map<PII,int>mp;
void add(int a,int b,int c)
{
    e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
void Dijkstra(int startx)
{
    memset(dist,0x3f,sizeof dist);
    dist[startx]=0;
    memset(st,0,sizeof st);
    priority_queue<PII,vector<PII>,greater<PII>> heap; heap.push({0,startx});
    while(heap.size())
    {
        auto t=heap.top(); heap.pop();
        int u=t.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];
                heap.push({dist[j],j});
            }
        }
    }
}
int main(void)
{
    memset(h,-1,sizeof h);
    cin>>n>>m;
    a[0]=1;
    for(int i=1;i<=5;i++) cin>>a[i];
    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 i=0;i<=5;i++)//预处理出所有的该点到其它亲戚点的最短距离表
    {
        Dijkstra(a[i]);
        for(int j=0;j<=5;j++)
        {
            mp[{a[i],a[j]}]=dist[a[j]];
            mp[{a[j],a[i]}]=dist[a[j]];
        }
    }
    int ans=1e9;
    sort(a+1,a+6);
    do//枚举所有的排列方式
    {
        int sum=0;
        for(int i=1;i<=5;i++) sum+=mp[{a[i-1],a[i]}];
        ans=min(ans,sum);
    }while(next_permutation(a+1,a+6));
    cout<<ans<<endl;
    return 0;
}

340. 通信线路【二分 + 最短路】

在这里插入图片描述
https://www.acwing.com/problem/content/342/
在这里插入图片描述
上图摘自: https://www.acwing.com/solution/content/36383/

#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
const int N=1e5+10;
int h[N],e[N*2],w[N*2],ne[N],idx;
int dist[N],st[N];
int n,m,k;
void add(int a,int b,int c) {e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;}
bool check(int mid)
{
    memset(dist,0x3f,sizeof dist);
    memset(st,0,sizeof st);
    dist[1]=0;
    priority_queue<PII,vector<PII>,greater<PII>> heap; heap.push({0,1});
    while(heap.size())
    {
        auto t=heap.top(); heap.pop();
        int u=t.second;
        if(st[u]) continue;
        st[u]=1;
        for(int i=h[u];i!=-1;i=ne[i])
        {
            int j=e[i],c=w[i]>mid?1:0;
            //看每一条边对于整条最短路的贡献,如果大于mid说明是有贡献的为1,否则没有贡献为0
            if(dist[j]>dist[u]+c)
            {
                dist[j]=dist[u]+c;
                heap.push({dist[j],j});
            }
        }
    }
    return dist[n]<=k;//大于mid的边数小于等于k,故满足说明还可以更优
}
int main(void)
{
    memset(h,-1,sizeof h);
    cin>>n>>m>>k;
    for(int i=0;i<m;i++)
    {
        int a,b,c; cin>>a>>b>>c;
        add(a,b,c),add(b,a,c);
    }
    int l=0,r=1e6+1;
    while(l<r)//二分答案
    {
        int mid=l+r>>1;
        if(check(mid)) r=mid;
        else l=mid+1;
    }
    if(r==1e6+1) puts("-1");//说明没有答案
    else cout<<l<<endl;
    return 0;
}

342. 道路与航线【未完成 / 拓扑排序 + Dijkstra】

在这里插入图片描述
https://www.acwing.com/problem/content/344/


341. 最优贸易【反向建图 spfa】

在这里插入图片描述
https://www.acwing.com/problem/content/343/
在这里插入图片描述
上图摘自:y总

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
const int M=1e6+10;
int hs[N],he[N],e[M],ne[M],idx;
int w[N],n,m;
int st[N];
int minv[N],maxv[N];
void add(int h[],int a,int b)
{
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void spfa(int h[],int dist[],int type)
{
    memset(st,0,sizeof st);
    queue<int> q;
    if(type)
    {
        memset(dist,-0x3f,sizeof minv);
        dist[n]=w[n]; st[n]=1;
        q.push(n);
    }else
    {
        memset(dist,0x3f,sizeof maxv);
        dist[1]=w[1]; st[1]=1;
        q.push(1);
    }
    while(q.size())
    {
        int u=q.front(); q.pop();
        st[u]=false;
        for(int i=h[u];i!=-1;i=ne[i])
        {
            int j=e[i];
            if(!type&&dist[j]>min(dist[u],w[j]))
            {
                dist[j]=min(dist[u],w[j]);
                if(!st[j]) q.push(j),st[j]=1;
            }
            if(type&&dist[j]<max(dist[u],w[j]))
            {
                dist[j]=max(dist[u],w[j]);
                if(!st[j]) q.push(j),st[j]=1;
            }
        }
    }
}
int main(void)
{
    memset(hs,-1,sizeof hs);
    memset(he,-1,sizeof he);
    cin>>n>>m;
    for(int i=1;i<=n;i++) cin>>w[i];
    for(int i=0;i<m;i++)
    {
        int a,b,op; cin>>a>>b>>op;
        add(hs,a,b),add(he,b,a);
        if(op==2) add(hs,b,a),add(he,a,b);
    }
    spfa(hs,minv,0);
    spfa(he,maxv,1);
    int ans=0;
    for(int i=1;i<=n;i++) ans=max(ans,maxv[i]-minv[i]);//枚举所有的点,求以该点作为交易点
    cout<<ans;
    return 0;
}

单源最短路的扩展应用

1137. 选择最佳线路【反向建图】

在这里插入图片描述
https://www.acwing.com/problem/content/1139/
方法一: 反向建图即可。

#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
const int N=1e3+10;
int dist[N],st[N],n,m,s;
int h[N],e[N*20],w[N*20],ne[N*20],idx;
int a[N],t;
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)
{
    memset(dist,0x3f,sizeof dist);
    memset(st,0,sizeof st);
    dist[s]=0;
    priority_queue<PII,vector<PII>,greater<PII>>heap; heap.push({0,s});
    while(heap.size())
    {
        auto t=heap.top(); heap.pop();
        int u=t.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];
                heap.push({dist[j],j});
            }
        }
    }
}
int main(void)
{
    while(cin>>n>>m>>s)
    {
        memset(h,-1,sizeof h);
        idx=0;
        for(int i=0;i<m;i++)
        {
            int a,b,c; cin>>a>>b>>c;
            add(b,a,c);
        }
        cin>>t;
        for(int i=0;i<t;i++) cin>>a[i];
        Dijkstra(s);
        int ans=0x3f3f3f3f;
        for(int i=0;i<t;i++) ans=min(ans,dist[a[i]]);
        if(ans==0x3f3f3f3f) puts("-1");
        else cout<<ans<<endl;
    }
    return 0;
}

方法二: 建立虚拟结点。

#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
const int N=1e4;
int dist[N],st[N],n,m,s;
int h[N],e[N*20],w[N*20],ne[N*20],idx;
int a[N],t;
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)
{
    memset(dist,0x3f,sizeof dist);
    memset(st,0,sizeof st);
    dist[s]=0;
    priority_queue<PII,vector<PII>,greater<PII>>heap; heap.push({0,s});
    while(heap.size())
    {
        auto t=heap.top(); heap.pop();
        int u=t.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];
                heap.push({dist[j],j});
            }
        }
    }
}
int main(void)
{
    while(cin>>n>>m>>s)
    {
        memset(h,-1,sizeof h);
        idx=0;
        for(int i=0;i<m;i++)
        {
            int a,b,c; scanf("%d%d%d",&a,&b,&c);
            add(a,b,c);
        }
        cin>>t;
        for(int i=0;i<t;i++) cin>>a[i],add(0,a[i],0);
        Dijkstra(0);
        int ans=dist[s];
        if(ans==0x3f3f3f3f) puts("-1");
        else cout<<ans<<endl;
    }
    return 0;
}

1131. 拯救大兵瑞恩【未完成 拆点 很重要可以试试】

在这里插入图片描述
https://www.acwing.com/problem/content/1133/

1134. 最短路计数【统计最短路的数量】

在这里插入图片描述
https://www.acwing.com/problem/content/1136/
无边权就是全部的边权为1。这里只能用bfs和Dijkstra不能用spfa。
因为bfs只入队一次,出队一次是一个拓扑序。
Dijkstra也是同样如此。而spfa则不是。因为这里的边权都为1故第一次到达就是最短路故可以用bfs

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
const int M=1e5*5+10;
const int mod=100003;
int h[N],e[M],ne[M],cnt[N],idx;
int n,m,dist[N];
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;
}

383. 观光【拆点 可以搞】

在这里插入图片描述
https://www.acwing.com/problem/content/385/

Floyd算法

floyd的应用场景

  • 1.最短路
  • 2.传递闭包
  • 3.找最小环
  • 4.恰好经过k条边的最短路,倍增。

1125. 牛的旅行【floyd一个好的扩展】

在这里插入图片描述
https://www.acwing.com/problem/content/1127/

#include<bits/stdc++.h>
using namespace std;
const int N=200;
const double INF=1e12;
double maxv[N],dist[N][N];
string s[N];
int n,x,y;
map<int, pair<double,double> >mp;
double get(int a,int b)
{
    int x=mp[a].first,y=mp[a].second;
    int xx=mp[b].first,yy=mp[b].second;
    return sqrt((x-xx)*(x-xx)+(y-yy)*(y-yy));
}
void init()
{
    for(int i=0;i<n;i++)    
    {
        for(int j=0;j<n;j++) 
            if(s[i][j]=='1') dist[i][j]=get(i,j);
            else if(i!=j) dist[i][j]=INF;
    }
}
void folyd()
{
    for(int k=0;k<n;k++)
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++) dist[i][j]=min(dist[i][j],dist[i][k]+dist[k][j]);
}
int main(void)
{
    cin>>n;
    for(int i=0;i<n;i++) cin>>x>>y,mp[i]={x,y};
    for(int i=0;i<n;i++) cin>>s[i];
    init(),folyd();
    double res1=0,res2=INF;
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<n;j++)
        {
            if(dist[i][j]>INF/2) continue;//不可达,说明不是一个集合
            maxv[i]=max(maxv[i],dist[i][j]);
            res1=max(res1,maxv[i]);
        }
    }
    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)
            if(dist[i][j]>INF/2) res2=min(res2,maxv[i]+get(i,j)+maxv[j]);
    printf("%.6lf",max(res1,res2));
    return 0;
}

343. 排序【floyd传递闭包】

在这里插入图片描述
https://www.acwing.com/problem/content/description/345/
g[i][j]==0 说明没关系 g[i][j]==1 说明 i<j

#include<bits/stdc++.h>
using namespace std;
const int N=30;
int g[N][N],n,m,st[N];
void folyd()
{
	for(int k=0;k<n;k++)
		for(int i=0;i<n;i++)
			for(int j=0;j<n;j++)
				g[i][j]|=g[i][k]&&g[k][j];
}
int check()
{
	for(int i=0;i<n;i++) if(g[i][i]) return 2;//矛盾
	for(int i=0;i<n;i++)
		for(int j=0;j<i;j++)
			if(!g[i][j]&&!g[j][i]) return 0;//还没有完整
	return 1;//完整了
}
char get_min()
{
	for(int i=0;i<n;i++)//枚举所有的可能的最小值
	{
		if(!st[i])
		{
			bool flag=true;
			for(int j=0;j<n;j++)
			{
				if(!st[j]&&g[j][i])//如果没选的有比其更小的
				{
					flag=false;
					break;
				}
			}
			if(flag) //说明没有比其更小的了
			{
				st[i]=true;
				return 'A'+i;
			}
		}
	}
}
int main(void)
{
    while(cin>>n>>m,n||m)
    {
        memset(g,0,sizeof g);
        memset(st,0,sizeof st);
        int type=0,t=0;
        for(int i=1;i<=m;i++)
        {
            string s;  cin>>s;    
            int a=s[0]-'A',b=s[2]-'A';
            if(!type)//还可以继续
            {
                g[a][b]=1;
                folyd();
                type=check();
                if(type) t=i;
            }
        }
        if(!type) puts("Sorted sequence cannot be determined.");
        else if (type == 2) printf("Inconsistency found after %d relations.\n", t);
        else
        {
        	printf("Sorted sequence determined after %d relations: ", t);
            for (int i = 0; i < n; i ++ ) printf("%c", get_min());
            printf(".\n");
        }
    }
    return 0;
}

344. 观光之旅【未完成】

在这里插入图片描述
https://www.acwing.com/problem/content/346/


345. 牛站【未完成】

在这里插入图片描述
https://www.acwing.com/problem/content/347/


最小生成树

1140. 最短网络【最基本的最小生成树的板子】

在这里插入图片描述
基本的板子没啥说的
prim()算法

#include<bits/stdc++.h>
using namespace std;
const int N=110;
int g[N][N],n,st[N],res;
int dist[N];
void prim()
{
    memset(dist,0x3f,sizeof dist);
    dist[1]=0;
    for(int i=0;i<n;i++)
    {
        int t=-1;
        for(int j=1;j<=n;j++) if(!st[j]&&(t==-1 || dist[j]<dist[t])) t=j;
        st[t]=1;
        res+=dist[t];
        for(int j=1;j<=n;j++) dist[j]=min(dist[j],g[t][j]);
    }
}
int main(void)
{
    cin>>n;
    memset(g,0x3f,sizeof g);
    for(int i=1;i<=n;i++) g[i][i]=0;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            cin>>g[i][j];
    prim();
    cout<<res;
    return 0;
}

kruskal算法

#include<bits/stdc++.h>
using namespace std;
const int N=110;
int a[N][N],n,p[N];
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;
    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))
        {
            res+=c;
            p[find(a)]=find(b);
        }
    }
    return res;
}
int main(void)
{
    cin>>n;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++) cin>>a[i][j];
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            if(i==j) continue;
            ve.push_back({i,j,a[i][j]});
            ve.push_back({j,i,a[i][j]});
        }
    }
    cout<<f();
    return 0;
}

1141. 局域网【最小生成树的一个简单的变种】

在这里插入图片描述
https://www.acwing.com/problem/content/1143/
总的和减去最小生成树的值即为答案。即加的是不是最小生成树上的边的和。

#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;
vector<node>ve;
int find(int x)
{
    if(x!=p[x]) p[x]=find(p[x]);
    return p[x];
}
int f()
{
    int res=0;
    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(b)]=find(a);
        else res+=c;
    }
    return res;
}
int main(void)
{
    cin>>n>>m;
    for(int i=1;i<=n;i++) p[i]=i;
    for(int i=0;i<m;i++)
    {
        int a,b,c; cin>>a>>b>>c;
        ve.push_back({a,b,c});
    }
    cout<<f()<<endl;
    return 0;
}

1142. 繁忙的都市 【简单的变种】

在这里插入图片描述
https://www.acwing.com/problem/content/1144/
就是求一下边数,和最小生成树上的最长的边。边数是n-1这是可以直接得到的。

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
struct node{int a,b,c;};
int n,m,cnt,maxv,p[N];
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 f()
{
    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))
        {
            cnt++;
            maxv=max(maxv,c);
            p[find(b)]=find(a);
        }
    }
}
int main(void)
{
    cin>>n>>m;
    for(int i=0;i<m;i++)
    {
        int a,b,c; cin>>a>>b>>c;
        ve.push_back({a,b,c});
    }
    f();
    cout<<cnt<<" "<<maxv;
    return 0;
}

1143. 联络员【简单的最小生成树的变种】

在这里插入图片描述
https://www.acwing.com/problem/content/1145/
先将必选的弄好,剩下的就是普通的板子。

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+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>ve1,ve2;
int find(int x)
{
    if(x!=p[x]) p[x]=find(p[x]);
    return p[x];
}
int solve()
{
    for(int i=1;i<=n;i++) p[i]=i;
    sort(ve1.begin(),ve1.end(),cmp);
    sort(ve2.begin(),ve2.end(),cmp);
    int sum=0;
    for(int i=0;i<ve1.size();i++)
    {
        int a=ve1[i].a,b=ve1[i].b,c=ve1[i].c;
        p[find(b)]=find(a);
        sum+=c;
    }
    for(int i=0;i<ve2.size();i++)
    {
        int a=ve2[i].a,b=ve2[i].b,c=ve2[i].c;
        if(find(a)!=find(b))
        {
            p[find(b)]=find(a);
            sum+=c;
        }
    }
    return sum;
}
int main(void)
{
    cin>>n>>m;
    for(int i=0;i<m;i++)
    {
        int op,a,b,c; cin>>op>>a>>b>>c;
        if(op==1) ve1.push_back({a,b,c});
        else ve2.push_back({a,b,c});
    }
    cout<<solve()<<endl;
    return 0;
}

1144. 连接格点【好的变种最小生成树】

在这里插入图片描述
https://www.acwing.com/problem/content/1146/
1144. 连接格点【最小生成树】

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int p[N],n,m;
int x,y,xx,yy;
struct node{int a,b,c;};
bool cmp(node a,node b) {return a.c<b.c;}
vector<node>ve1,ve2;
int find(int x)
{
    if(x!=p[x]) p[x]=find(p[x]);
    return p[x];
}
int get(int x,int y)
{
    return x*m+y;
}
void solve(int x,int y)//存所有的边
{
    int dx[4]={-1,0,1,0};
    int dy[4]={0,1,0,-1};
    for(int i=0;i<4;i++)
    {
        int tempx=x+dx[i];
        int tempy=y+dy[i];
        if(tempx<1||tempx>n||tempy<1||tempy>m) continue;
        if(get(x-1,y)<get(tempx-1,tempy))//a<b时才加可以避免重复加
        {
            if(dy[i]) ve2.push_back({get(x-1,y),get(tempx-1,tempy),2});
            else ve1.push_back({get(x-1,y),get(tempx-1,tempy),1});
        }
    }
}
void init()//初始化
{
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++) p[get(i-1,j)]=get(i-1,j);
}
int f()
{
    int res=0;
    for(int i=0;i<ve1.size();i++)
    {
        int a=ve1[i].a,b=ve1[i].b,c=ve1[i].c;
        if(find(a)!=find(b)) 
        {
            p[find(a)]=find(b);
            res+=c;
        }
    }
    for(int i=0;i<ve2.size();i++)
    {
        int a=ve2[i].a,b=ve2[i].b,c=ve2[i].c;
        if(find(a)!=find(b)) 
        {
            p[find(a)]=find(b);
            res+=c;
        }
    }
    return res;
}
int main(void)
{
    scanf("%d%d",&n,&m);
    init();
    while(scanf("%d%d%d%d",&x,&y,&xx,&yy)!=EOF)
    {
        int a=get(x-1,y),b=get(xx-1,yy);
        if(find(a)!=find(b)) p[find(a)]=find(b);
    }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++) solve(i,j);
    printf("%d\n",f());
    return 0;
}

最小生成树的扩展应用

1146. 新的开始【虚拟节点 和 最小生成树】

在这里插入图片描述
https://www.acwing.com/problem/content/1148/

#include<bits/stdc++.h>
using namespace std;
struct node{int a,b,c;};
bool cmp(node a,node b) {return a.c<b.c;}
const int N=1e3+10;
int a[N],p[N],n,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;
    for(int i=0;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;
        }
    }
    return res;
}
int main(void)
{
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i],ve.push_back({0,i,a[i]});//创建虚拟结点
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++) 
        {
            cin>>c;
            if(c) ve.push_back({i,j,c});
        }
    cout<<f()<<endl;
    return 0;
}

1145. 北极通讯网络【扩展】

在这里插入图片描述
https://www.acwing.com/problem/content/description/1147/
本质就是删除最短路上的(k-1)条边 输出第k大的边

#include<bits/stdc++.h>
using namespace std;
const int N=510;
int n,k,p[N];
struct node
{
    int a,b;
    double c;
};
int find(int x)
{
    if(x!=p[x]) p[x]=find(p[x]);
    return p[x];
}
bool cmp(node a,node b) {return a.c<b.c;}
vector<node>ans;
vector<pair<double,double>>ve;
double get(int x,int y,int xx,int yy){return sqrt(1.0*(x-xx)*(x-xx)+(y-yy)*(y-yy));}
double solve()
{
    for(int i=0;i<n;i++) p[i]=i;
    sort(ans.begin(),ans.end(),cmp);
    vector<double>s;//存一下所有的边
    for(int i=0;i<ans.size();i++)
    {
        int a=ans[i].a,b=ans[i].b;
        double c=ans[i].c;
        if(find(a)!=find(b))
        {
            p[find(a)]=find(b);
            s.push_back(c);
        }
    }
    if(k==n) return 0;
    reverse(s.begin(),s.end());
    return s[k-1];//输出从大到小第k的边
}
int main(void)
{
    cin>>n>>k;
    for(int i=0;i<n;i++)
    {
        double x,y; cin>>x>>y;
        ve.push_back({x,y});
    }
    for(int i=0;i<ve.size();i++)
    {
        for(int j=i+1;j<ve.size();j++)
        {
            double x=ve[i].first,y=ve[i].second;
            double xx=ve[j].first,yy=ve[j].second;
            ans.push_back({i,j,get(x,y,xx,yy)});
        }
    }
    printf("%.2lf",solve());
    return 0;
}

346. 走廊泼水节【经典题 求构成完全图的最小权和】

在这里插入图片描述
https://www.acwing.com/problem/content/348/

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int p[N],cnt[N],t,n;
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 solve()
{
    for(int i=1;i<=n;i++) p[i]=i,cnt[i]=1;
    sort(ve.begin(),ve.end(),cmp);
    int sum=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))
        {
            int pa=find(a),pb=find(b);
            sum+=(cnt[pa]*cnt[pb]-1)*(c+1);
            //俩连通块构成完全图 cnt[pa]*cnt[pb]-1 
            //-1是因为去除已经有的那条边
            cnt[pb]+=cnt[pa];
            p[pa]=pb;
        }
    }
    return sum;
}
int main(void)
{
    cin>>t;
    while(t--)
    {
        cin>>n;
        ve.clear();
        for(int i=1;i<=n-1;i++) 
        {
            int a,b,c; cin>>a>>b>>c;
            ve.push_back({a,b,c});
        }
        cout<<solve()<<endl;
    }
    return 0;
}

1148. 秘密的牛奶运输【次小生成树 未完成】

在这里插入图片描述
https://www.acwing.com/problem/content/description/1150/
在这里插入图片描述


负环

求负环的常用方法,基于SPFA:

  • (1)拥挤每个点入队的次数,如果某个点入队n次,则说明存在负环。
  • (2)统计当前每个点的最短路中所包含的变数,如果某个点的最短路所包含的变数
    大于等于n,则说明存在环。

剪枝:当所有的点的入队次数超过2n时,我们就认为图中有很大可能是存在负环的。

904. 虫洞【变种的判负环】

在这里插入图片描述
https://www.acwing.com/problem/content/906/

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int h[N],e[N],w[N],ne[N],idx;
int dist[N];
int t,n,m1,m2; 
void add(int a,int b,int c)
{
    e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
void init()
{
    memset(h,-1,sizeof h);
    idx=0;
}
bool spfa()
{
    queue<int>q;
    int cnt[N]={0};
    int st[N]={0};
    memset(dist,0,sizeof dist);
    for(int i=1;i<=n;i++) q.push(i),st[i]=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];
                cnt[j]=cnt[u]+1;
                if(cnt[j]>=n) return true;
                if(!st[j]) q.push(j);
            }
        }
    }
    return false;
}
int main(void)
{
    cin>>t;
    while(t--)
    {
        init();
        cin>>n>>m1>>m2;
        while(m1--)
        {
            int a,b,c; cin>>a>>b>>c;
            add(a,b,c),add(b,a,c);
        }
        while(m2--)
        {
           int a,b,c; cin>>a>>b>>c;
           add(a,b,-c);
        }
        if(spfa()) puts("YES");
        else puts("NO");
    }
    return 0;
}

361. 观光奶牛【判断正环 + 二分】

在这里插入图片描述
https://www.acwing.com/problem/content/363/
详细题解

#include<bits/stdc++.h>
using namespace std;
const int N=1e4+10;
int h[N],e[N],wt[N],ne[N],idx;
int st[N],wf[N],cnt[N],n,m;
double dist[N];
void add(int a,int b,int c)
{
    e[idx]=b,wt[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
bool check(double mid)
{
    memset(dist,0,sizeof dist);
    memset(st,0,sizeof st);
    memset(cnt,0,sizeof cnt);
    queue<int>q;
    for(int i=1;i<=n;i++) q.push(i),st[i]=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]+wf[u]-mid*wt[i])
            {
                dist[j]=dist[u]+wf[u]-mid*wt[i];
                if(!st[j]) q.push(j);
                cnt[j]=cnt[u]+1;
                if(cnt[j]>=n) return true;
            }
        }
    }
    return false;
}
int main(void)
{
    memset(h,-1,sizeof h);
    cin>>n>>m;
    for(int i=1;i<=n;i++) cin>>wf[i];
    while(m--)
    {
        int a,b,c; cin>>a>>b>>c;
        add(a,b,c);
    }
    double l=0,r=1000;
    while(r-l>1e-4)
    {
        double mid=(l+r)/2;
        if(check(mid)) l=mid;
        else  r=mid;
    }
    printf("%.2lf",l);
    return 0;
}

1165. 单词环【未完成】

在这里插入图片描述
https://www.acwing.com/problem/content/1167/


差分约束

在这里插入图片描述

1169. 糖果

在这里插入图片描述
https://www.acwing.com/problem/content/description/1171/
在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
typedef long long int LL;
int h[N],e[N],w[N],ne[N],idx;
int dist[N],st[N],cnt[N];
int n,m;
void add(int a,int b,int c) {e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;}
bool spfa()
{
    stack<int>q; q.push(0);
    memset(dist,-0x3f,sizeof dist);
    dist[0]=0,st[0]=1;
    while(q.size())
    {
        int u=q.top(); 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];
                cnt[j]=cnt[u]+1;
                if(cnt[j]>=n+1) return false;
                if(!st[j]) q.push(j),st[j]=1;
            }
        }
    }
    return true;
}
int main(void)
{
    memset(h,-1,sizeof h);
    cin>>n>>m;
    for(int i=0;i<m;++i)
    {
        int op,a,b; scanf("%d%d%d",&op,&a,&b);
        if(op==1) add(a,b,0),add(b,a,0);
        else if(op==2) add(a,b,1);
        else if(op==3) add(b,a,0);
        else if(op==4) add(b,a,1);
        else add(a,b,0);
    }
    for(int i=1;i<=n;++i) add(0,i,1);
    if(!spfa()) puts("-1");
    else
    {
        LL res=0;
        for(int i=1;i<=n;++i) res+=dist[i];
        cout<<res;
    }
    return 0;
}

有向图的强连通分量

对于一个有向图,**连通分量:**对于分量中任意两点u,v。必然可以从u走到v,且从v走到u。
强联通分量: 极大的联通分量。
求联通分量常用的算法模板是:tarjan算法

拓扑排序

1191. 家谱树【基本的拓扑的板子】

在这里插入图片描述
https://www.acwing.com/problem/content/1193/

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int h[N],e[N],ne[N],d[N],idx;
void add(int a,int b) {e[idx]=b,ne[idx]=h[a],h[a]=idx++;}
int n,m;
vector<int>ans;
void topsort()
{
    queue<int>q;
    for(int i=1;i<=n;i++) if(!d[i]) q.push(i),ans.push_back(i);
    while(q.size())
    {
        int u=q.front(); q.pop();
        for(int i=h[u];i!=-1;i=ne[i])
        {
            int j=e[i];
            if(--d[j]==0) q.push(j),ans.push_back(j);
        }
    }
}
int main(void)
{
    memset(h,-1,sizeof h);
    cin>>n;
    for(int i=1;i<=n;i++) while(cin>>m,m) add(i,m),d[m]++;
    topsort();
    for(int i=0;i<ans.size();i++) cout<<ans[i]<<" ";
    return 0;
}

1192. 奖金

在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int h[N],e[N],ne[N],idx;
int st[N],cnt[N],n,m,ans[N];
void add(int a,int b) 
{
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
vector<int>ve;
bool topsort()
{
    queue<int>q;
    for(int i=1;i<=n;i++) if(!cnt[i]) q.push(i),ve.push_back(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];
            if(--cnt[j]==0)
            {
                if(!st[j]) q.push(j),ve.push_back(j),st[j]=1;
            }
        }
    }
    if(ve.size()==n) return true;
    return false;
}
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);
        cnt[b]++;
    }
    if(topsort())
    {
        int sum=0;
        for(int i=ve.size()-1;i>=0;i--)
        {
            ans[ve[i]]=100;
            for(int j=h[ve[i]];j!=-1;j=ne[j]) ans[ve[i]]=max(ans[ve[i]],ans[e[j]]+1);
            sum+=ans[ve[i]];
        }
        cout<<sum;
    }else puts("Poor Xed");
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值