Week 8 最短路径问题

 1.B3647 【模板】Floyd

这个Floyd算法蒟蒻也似懂非懂,建议去看本题题解: B3647 【模板】Floyd - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)icon-default.png?t=N7T8https://www.luogu.com.cn/problem/solution/B3647

#include <bits/stdc++.h>
using namespace std;

int n,m,u,v,w;
int Map[105][105],g[105][105];

int main()
{
    cin>>n>>m;
    //初始化
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++){
            if(i == j) Map[i][j] = 0;
            else{
                Map[i][j] = 1e9;
                Map[j][i] = 1e9;
            }
        }

    //存图
    for(int i=0;i<m;i++){
        cin>>u>>v>>w;
        Map[u][v] = min(Map[u][v],w);
        Map[v][u] = Map[u][v];
    }

    for(int k=1;k<=n;k++){
        for(int u=1;u<=n;u++){
            for(int v=1;v<=n;v++){
                Map[u][v] = min(Map[u][v],Map[u][k]+Map[k][v]);
            }
        }
    }

    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++)
            cout<<Map[i][j]<<" ";
        cout<<endl;
    }
    return 0;
}

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

堆优化蒟蒻也不太懂,就不瞎解释了。

迪杰斯特拉dijkstra算法,重点——松弛操作!其余请看图(

#include<bits/stdc++.h>

const int MaxN = 100010, MaxM = 500010;

struct edge
{
    int to, dis, next;
};

edge e[MaxM];
int head[MaxN], dis[MaxN], cnt;
bool vis[MaxN];
int n, m, s;

inline void add_edge( int u, int v, int d )
{
    cnt++;
    e[cnt].dis = d;
    e[cnt].to = v;
    e[cnt].next = head[u];
    head[u] = cnt;
}

struct node
{
    int dis;
    int pos;
    bool operator <( const node &x )const
    {
        return x.dis < dis;
    }
};

std::priority_queue<node> q;


inline void dijkstra()
{
    dis[s] = 0;
    q.push( ( node ){0, s} );
    while( !q.empty() )
    {
        node tmp = q.top();
        q.pop();
        int x = tmp.pos, d = tmp.dis;
        if( vis[x] )
            continue;
        vis[x] = 1;
        for( int i = head[x]; i; i = e[i].next )
        {
            int y = e[i].to;
            if( dis[y] > dis[x] + e[i].dis )
            {
                dis[y] = dis[x] + e[i].dis;
                if( !vis[y] )
                {
                    q.push( ( node ){dis[y], y} );
                }
            }
        }
    }
}


int main()
{
    scanf( "%d%d%d", &n, &m, &s );
    for(int i = 1; i <= n; ++i)dis[i] = 0x7fffffff;
    for( register int i = 0; i < m; ++i )
    {
        register int u, v, d;
        scanf( "%d%d%d", &u, &v, &d );
        add_edge( u, v, d );
    }
    dijkstra();
    for( int i = 1; i <= n; i++ )
        printf( "%d ", dis[i] );
    return 0;
}

3.P2661 [NOIP2015 提高组] 信息传递

并查集求最小环并查集是一种树型的数据结构,用于处理一些不相交集合的合并及查询问题(即所谓的并、查)。比如说,我们可以用并查集来判断一个森林中有几棵树、某个节点是否属于某棵树等。

把每个同学看成一个节点,信息传递为一条有向边,游戏轮数即为最小环。

#include <bits/stdc++.h>
using namespace std;

const int MAX_N = 2e5+5;
int n,T[MAX_N],f[MAX_N],cnt[MAX_N];//f[]保存祖先节点,cnt[]保存到祖先的路径长
int min_x = 0x7777777;

int father(int x)//用于查找祖先节点
{
    if(f[x] != x){
        int fa = f[x];//记录父节点,防止被递归更新掉
        f[x] = father(f[x]);//更新祖先节点
        cnt[x] += cnt[fa];
    }
    return f[x];
}

void check(int a,int b)
{
    int x = father(a),y = father(b);//查找祖先节点
    if(x != y){//若不相连,则连接两点,更新父节点和路径长。
        f[x] = y;
        cnt[a] = cnt[b]+1;
    }
    else//若已连接,则更新最小环长度。
        min_x = min(min_x,cnt[a]+cnt[b]+1);
}

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++) f[i] = i;//父节点初始化为自己,cnt=0
    for(int i=1;i<=n;i++)
        scanf("%d",&T[i]);

    for(int i=1;i<=n;i++){
        check(i,T[i]);
    }
    printf("%d",min_x);
    return 0;
}

4.P1144 最短路计数

在题解中看到一个新奇的思路,无权无向图中,顶点到某个节点的最短路就是其BFS搜索树的深度。

#include <bits/stdc++.h>
using namespace std;

const int maxn=1e6+5,maxm=2e6+5,mod=100003;
int n,m;
vector<int> Map[maxn];
int deep[maxn],cnt[maxn],vis[maxn];//deep[]当前顶点到第i点的深度,cnt[]顶点到第i点的min深度,vis[]标记某点是否来过

int main()
{
    cin>>n>>m;
    for(int i=1;i<=m;i++){
        int x,y;
        cin>>x>>y;
        Map[x].push_back(y);
        Map[y].push_back(x);//无向图存图
    }

    queue<int> q;
    //初始化
    deep[1] = 0;
    vis[1] = 0;
    q.push(1);
    cnt[1] = 1;

    while(!q.empty()){
        int x = q.front();
        q.pop();
        for(int i=0;i<Map[x].size();i++){
            int t = Map[x][i];
            if(!vis[t]){
                vis[t] = 1;
                deep[t] = deep[x]+1;
                q.push(t);
            }
            if(deep[t] == deep[x]+1){
                cnt[t] = (cnt[t]+cnt[x])%mod;
            }
        }
    }
    
    cnt[1] = 1;//也不知道哪里出了点问题,第一个点到自身的路径数错了,就强行改了一下
    for(int i=1;i<=n;i++)
        cout<<cnt[i]<<endl;
    return 0;
}

5.P8794 [蓝桥杯 2022 国 A] 环境治理

原题指路:P8794 [蓝桥杯 2022 国 A] 环境治理 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)icon-default.png?t=N7T8https://www.luogu.com.cn/problem/P8794

二分+最短路

二分:每治理一次,P值都会下降,为单调递减,可用二分。

最短路:floyd算法,详见第一题。

#include <bits/stdc++.h>
using namespace std;

#define inf 0x7777777
int n,q,ans=-1;
long long d[105][105],l[105][105];

int cal(int x)//计算治理x天后的P值
{
    int temp[105][105];
    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)
            temp[i][j] = d[i][j];

    for(int i=0;i<n;i++){
        int a;
        if(x%n>=i+1) a = 1;
        else a = 0;
        int b = x/n+a;
        for(int j=0;j<n;j++){
            temp[i][j] -= b;
            if(temp[i][j]<l[i][j])
                temp[i][j] = l[i][j];

            temp[j][i] -= b;
            if(temp[j][i]<l[j][i])
                temp[j][i] = l[j][i];
        }
    }

    for(int k=0;k<n;k++)//floyed 算法求最短路径
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
                temp[i][j] = min(temp[i][j],temp[i][k]+temp[k][j]);
                
    int res=0;//计算P值
    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)
                res += temp[i][j];

    return res;
}

int main()
{
    cin>>n>>q;
    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)
            cin>>d[i][j];
    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)
            cin>>l[i][j];

    int left=0,right=inf;
    while(left<=right){//二分求治理天数
        int mid = (left+right)/2;
        if(cal(mid)<=q){
            right = mid-1;
            ans = mid;
        }
        else
            left = mid+1;
    }

    cout<<ans;
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

棠梨下

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值