IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> C++题解:[NOIP2013] 货车运输 -> 正文阅读

[数据结构与算法]C++题解:[NOIP2013] 货车运输

??????目录

题目?

题解


题目?

A 国有?n?座城市,编号从?1?到?n,城市之间有?m?条双向道路。每一条道路对车辆都有重量限制,简称限重。现在有?q?辆货车在运输货物,司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物。

输入格式

输入文件第一行有两个用一个空格隔开的整数?n,m,表示 A 国有?n?座城市和?m?条道路。

接下来?m?行每行?3?个整数?x、y、z,每两个整数之间用一个空格隔开,表示从?x?号城市到?y?号城市有一条限重为?z?的道路。注意:x?不等于?y,两座城市之间可能有多条道路。

接下来一行有一个整数?q,表示有?q?辆货车需要运货。

接下来?q?行,每行两个整数?x、y,之间用一个空格隔开,表示一辆货车需要从?x?城市 运输货物到?y?城市,注意:x?不等于?y。

输出格式

输出共有?q?行,每行一个整数,表示对于每一辆货车,它的最大载重是多少。如果货车不能到达目的地,输出 -1。

数据范围

0<n<10,000,0<m<50,000,0<q<30,000,0≤z≤100,000。

输出时每行末尾的多余空格,不影响答案正确性

要求使用「文件输入输出」的方式解题,输入文件为?truck.in,输出文件为?truck.out

样例输入

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

样例输出

3
-1
3

题解:

知识点:链式前向星、并查集、最大生成树、深度优先搜索、动态规划、最近公共祖先

??

?

分析:

我们首先将问题简单化,考虑只有一次询问,可以把边从大到小排序然后逐渐加进来,直到?u,v?连通(读者可以画画图,肯容易得到)?

那么多次询问每次我们就都可以重复这样的步骤,我们发现如果一条边加进来的时候,它的两个端点早就连通了,那这条边就是没有意义的

所以有意义的边恰好构成了一颗最大生成树(MST)(读者可以画画图,肯容易得到)

那么对于每次询问我们只需要在最大生成树上求出路径上的最小边就可以了。这个就可以用倍增(ST算法)法求LCA来做

对于每一次询问输出答案时就可以在求 LCA?的过程中得到?u?到?lca(u,v)(表示u、v的最近公共祖先) 的限重,v?到?lca(u,v)?的限重,取较小者即可

我们先用Kruskal做MST,再用ST做LCA,LCA见https://blog.csdn.net/Keven_11/article/details/108201627?

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define _for(i,a,b) for (int i=a;i<=b;i++)
using namespace std;
const int N=1e4+5,M=5e4+5;//g[u][j]表示u到f[u][j]这条链上边权的最大值。
int n,m,fa[N],h[N],e[M],ne[M],w[M],idx,d[N],f[N][20],g[N][20];
struct node{
    int a,b,w;
    bool operator <(const node& rhs) const{
        return w>rhs.w;
    }
}a[M];
inline void c_plus(){//可以忽略
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
}
inline void init_list(){//单链表
    idx=0;
    memset(h,-1,sizeof(h));
}
inline void add(int a,int b,int c){//同上
    e[idx]=b;
    w[idx]=c;
    ne[idx]=h[a];
    h[a]=idx++;
}
inline void init_set(){//并查集
    _for(i,1,n){
        fa[i]=i;
    }
}
int get(int x){//同上
    if (x==fa[x]){
        return x;
    }
    return fa[x]=get(fa[x]);
}
void kruskal(){//kruskal算法
    sort(a+1,a+m+1);
    _for(i,1,m){
        int x=get(a[i].a);
        int y=get(a[i].b);
        if (x!=y){
            fa[y]=x;
            add(a[i].a,a[i].b,a[i].w);
            add(a[i].b,a[i].a,a[i].w);
        }
    }
}
void dfs(int u){
	d[u]=d[f[u][0]]+1;
	for (int i=h[u];i!=-1;i=ne[i]){
		int j=e[i];
		if (j==f[u][0]){
			continue;
        }
		f[j][0]=u;
        g[j][0]=w[i];//我们需要在dfs函数里给g[v][0]赋初值,为每个结点v上面那条边的权值。
		dfs(j);
	}
}
int lca(int x,int y){//LCA算法
    if (get(x)!=get(y)){//判断是否合法,在不在一个连通块内
        return -1;
    }
	if (d[x]<d[y]){
		swap(x,y);
	}
	int k=(int)(log2(d[x])),ans=0x7fffffff;//ans存答案
	for (int j=k;j>=0;j--){
		if (d[f[x][j]]>=d[y]){
            ans=min(ans,g[x][j]);//维护最大值
			x=f[x][j];
		}
	}
	if (x==y){/
		goto output;
	}
	k=(int)(log2(d[x]));
	for (int j=k;j>=0;j--){
		if (f[x][j]!=f[y][j]){
            ans=min(ans,min(g[x][j],g[y][j]));//维护最大值
			x=f[x][j];
			y=f[y][j];
		}
	}
    ans=min(ans,min(g[x][0],g[y][0]));//最后还差一步才到达lca,因此还要算上最后两条边的最大值
    output:
	return ans;
}
int main(){
    freopen("truck.in","r",stdin);
    freopen("truck.out","w",stdout);
    c_plus();
    int q,x,y;
    cin>>n>>m;
    init_list();
    init_set();
    _for(i,1,m){
        cin>>a[i].a>>a[i].b>>a[i].w;
    }
    kruskal();
    _for(i,1,n){
        if (d[i]==0){//每个连通块都要遍历
            dfs(i);
        }
    }
    for (int j=1;(1<<j)<=n;j++){
        _for(i,1,n){
            f[i][j]=f[f[i][j-1]][j-1];
            g[i][j]=min(g[i][j-1],g[f[i][j-1]][j-1]);//通过动态规划计算g数组剩余的值。
//转移方程很显然,就是把从i到f[i][j]这条链,拆成i到f[i][j-1]和f[i][j-1]到f[i][j]两条链,
//然后取最大值
        }
    }
    cin>>q;
    while (q--){
        cin>>x>>y;
        cout<<lca(x,y)<<endl;
    }
    return 0;
}

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2021-08-25 12:27:44  更:2021-08-25 12:29:43 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 23:29:45-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码