• NOIP2013 货车运输


    3.货车运输

    (truck.cpp/c/pas)

    【问题描述】

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

    【输入】

    输入文件名为 truck.in。

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

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

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

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

    【输出】

    输出文件名为 truck.out。

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

    【输入输出样例】

    truck.in

    truck.out

    4

    3

    3

    1

    2

    4

    -1

    2

    3

    3

    3

    3

    1

    1

    3

    1

    3

    1

    4

    1

    3

               

    【数据说明】

    对于 30%的数据,0 < n < 1,000,0 < m < 10,000,0 < q< 1,000;

    对于 60%的数据,0 < n < 1,000,0 < m < 50,000,0 < q< 1,000;

    对于 100%的数据,0 < n < 10,000,0 < m < 50,000,0 < q< 30,000,0 ≤ z ≤ 100,000。

    【思路】

      MST+LCA

      可以知道如果运最大重量的货物,货车所走的一定是最大生成树上的边。

      方法:kruskal求解最大生成树。

      对于每个询问uv,找到两者的最近公共祖先r,那么货车走过的路线就是u->r->v,所以只需要在寻找LCA的时候比较路上的最小边即可。

      方法:暴力。先将uv挪到相同深度上来,然后并行向上寻找公共祖先。

      注意:当uv不属于同一个树中的时候意味着两者不互通,需要提前判断。

    【代码】

      1 #include<iostream>
      2 #include<vector>
      3 #include<cstring>
      4 #include<algorithm>
      5 #define FOR(a,b,c) for(int a=(b);a<(c);a++)
      6 using namespace std;
      7 
      8 const int maxn = 10000+10, maxm=50000+10;
      9 int n,m;
     10 int father[maxn];  //并查集 
     11 struct Edge{
     12     int u,v,d;
     13     bool operator <(const Edge& rhs) const{  //最大生成树 
     14       return d>rhs.d;
     15     }
     16 };
     17 struct Edge2{
     18     int v,d,next;
     19 };
     20 inline int find(int u) {
     21     return u==father[u]? u:father[u]=find(father[u]);
     22 }
     23 struct LCA{
     24     int first[maxm];   //链表式存初图 
     25     Edge2 e[maxm]; int en;
     26      
     27     int dist[maxn][maxn];  //[][]
     28     int d[maxn];          //深度数组 
     29     int p[maxn];          //p数组记录父节点 
     30     int root;
     31     
     32     void init() {
     33         en=0; root=n/2;          //root的选取会影响时间 
     34         for(int i=0;i<n;i++) first[i]=-1;
     35         d[0]=0; 
     36     }
     37     void AddEdge(int u,int v,int d){
     38         ++en;
     39         e[en].v=v; e[en].d=d;
     40         e[en].next=first[u];
     41         first[u]=en;
     42     }
     43     void build_tree(int u,int fa) {      //have failed
     44     //根据MST得出的初图(edges)建树->depth[] parent[] dist[][]
     45     //无根树->有根树 
     46            for(int i=first[u];i>0;i=e[i].next)  {  //i=next[i]!!!
     47                int v=e[i].v;
     48                if(v!=fa) {
     49                  d[v]=d[u]+1; dist[u][v]=dist[v][u]=e[i].d;
     50                  build_tree(v,p[v]=u);    //v!=fa 
     51            }
     52            }
     53     }
     54     void Move(int& u,int depth,int &ans){  //u溯回到高度为depth的祖先的位置 
     55         while(d[u]!=depth) { ans=min(ans,dist[u][p[u]]); u=p[u]; }
     56     }
     57     int query(int u,int v) {
     58         if(find(u) != find(v)) return -1; //uv之间不可达
     59         int ans=1<<30;
     60         if(d[u]<d[v]) Move(v,d[u],ans); else if(d[u]>d[v]) Move(u,d[v],ans); 
     61         while(u != v) {
     62             ans=min( ans , min(dist[u][p[u]],dist[v][p[v]]) ); 
     63             u=p[u]; v=p[v];
     64         }
     65         return ans;
     66     }
     67 };
     68 LCA lca;
     69 struct Kruskal{
     70     vector<Edge> edges;
     71     
     72     void init() {
     73         edges.clear();
     74         for(int i=0;i<n;i++) father[i]=i;
     75     }
     76     void AddEdge(int u,int v,int d) {
     77         edges.push_back((Edge){u,v,d});
     78     }
     79     void MST() {
     80         lca.init();
     81         sort(edges.begin(),edges.end());
     82         int cnt=0;
     83         int nc=edges.size();
     84         for(int i=0;i<nc;i++) {
     85             int u=edges[i].u,v=edges[i].v,d=edges[i].d; 
     86             int x=find(u),y=find(v);
     87             if(x!=y) {
     88                 lca.AddEdge(u,v,d);   //利用MST中的边构造lca 
     89                 lca.AddEdge(v,u,d);   //反向边 
     90                 father[x]=y;
     91                 if(++cnt==n-1) return ;  //判断有n-1条边提前结束 
     92             }
     93         }
     94     }
     95 };
     96 
     97 Kruskal krus;
     98 
     99 int main() {
    100     ios::sync_with_stdio(false);
    101     cin>>n>>m;
    102     krus.init();
    103     FOR(i,0,m) { //uv 0..
    104         int u,v,d;
    105         cin>>u>>v>>d; u--; v--; 
    106         krus.AddEdge(u,v,d);
    107     }
    108     krus.MST();
    109     lca.build_tree(lca.root,-1);
    110     int q; cin>>q;
    111     FOR(i,0,q) {
    112         int u,v; cin>>u>>v; u--; v--;
    113         cout<<lca.query(u,v)<<"
    ";
    114     }
    115     return 0;
    116 }
  • 相关阅读:
    Linux 安装中文man手册
    centos6.9使用NTFS-3G挂载ntfs文件系统
    Linux基础知识之挂载详解(mount,umount及开机自动挂载)
    技术点总结
    SQL 分组后获取其中一个字段最大值的整条记录 【转载】
    线程池之ThreadPool类与辅助线程
    Task.Run使用默认线程池
    VS生成事件
    线程池之ThreadPoolExecutor使用
    Sql笔记
  • 原文地址:https://www.cnblogs.com/lidaxin/p/4859286.html
Copyright © 2020-2023  润新知