zoukankan      html  css  js  c++  java
  • 题解报告——货车运输

    题目描述

    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。

    输入输出样例

    输入样例#1:
    4 3
    1 2 4
    2 3 3
    3 1 1
    3
    1 3
    1 4
    1 3
    输出样例#1: 
    3
    -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。


    【思路分析】 

    由于我们这道题只需要知道最大载货量,对于路径的长度没有要求的,所以我们只需要在最大的边上跑就好。那么怎么找最大边,那么就将最小生成树改一下,将边排序时从小到大改为从大到小,变成最大生成树,就可以在保证当前图中的每个点联通并且边权最大,就可以省去跑一些权值小的边的时间,然后就在这个图上跑LCA,跑LCA的时候顺便统计到公共祖先的路径上的最小边,然后输出就好。

    值得注意的是这个地方的点可能不是一个连通图,可能有多个,所以要在多个树上跑LCA,不过就是一个for循环的事

    最好先看看倍增LCA,只要会倍增LCA,那么这道题写出代码还是很快的

    具体看代码!

    【代码实现】 

      1 #include<cstdio>
      2 #include<vector>
      3 #include<cmath>
      4 #include<algorithm>
      5 #include<cstring>
      6 using namespace std;
      7 const int MAXN=1e6+7;
      8 struct sd{
      9     int ww,id;//记录到哪个节点,路径权值是多少
     10 }jump[10005][21],fa[10005];//用于跑LCA的结构体
     11 struct sd1{
     12     int next,to,w;
     13 }edge[50005];//用于最大生成树的结构体
     14 int ff[10005];//并查集
     15 int deep[10005];
     16 bool vis[10005];
     17 vector<sd> son[10005];//记录跑LCA的树
     18 vector<sd> link[10005];//记录之前的无向图
     19 int n,m,q;
     20 void find(int v)//倍增LCA
     21 {
     22     for(int i=1;i<=20;i++)
     23     {
     24         int temp=jump[v][i-1].id;
     25         jump[v][i].id=jump[temp][i-1].id;
     26         jump[v][i].ww=min(jump[v][i-1].ww,jump[temp][i-1].ww);
     27     }
     28 }
     29 void pre(int v)//LCA的预处理
     30 {
     31     deep[v]=deep[fa[v].id]+1;
     32     jump[v][0].id=fa[v].id;
     33     jump[v][0].ww=fa[v].ww;
     34     find(v);
     35     int ss=son[v].size();
     36     if(ss==0) 
     37     return;
     38     for(int i=0;i<ss;i++)
     39         pre(son[v][i].id);
     40 }
     41 int lca(int a,int b)//跑LCA
     42 {
     43     if(deep[b]>deep[a]) {int t=a;a=b;b=t;}
     44     int len=deep[a]-deep[b];
     45     int ans=MAXN;
     46     if(len!=0)
     47     {
     48         for(int i=20;i>=0;i--)
     49         {
     50             if(deep[jump[a][i].id]>=deep[b])
     51             {
     52                 ans=min(ans,jump[a][i].ww);
     53                 a=jump[a][i].id;
     54             }
     55         }
     56     }
     57     if(a==b) return ans;//如果两个节点属于祖先与后代关系,就不用继续了
     58     for(int i=20;i>=0;i--)
     59     {
     60         if(jump[a][i].id!=jump[b][i].id)
     61         {
     62             ans=min(jump[a][i].ww,ans);
     63             ans=min(jump[b][i].ww,ans);
     64             a=jump[a][i].id,b=jump[b][i].id;
     65         }
     66     }
     67     ans=min(ans,fa[a].ww);
     68     ans=min(ans,fa[b].ww);
     69     return ans;
     70 }
     71 bool cmp(sd1 a,sd1 b)
     72 {
     73     return a.w>b.w;
     74 }
     75 int father(int v)
     76 {
     77     if(ff[v]!=v) return ff[v]=father(ff[v]);
     78     return v;
     79 }
     80 void dfs(int v)//预处理建立跑LCA的图
     81 {
     82     vis[v]=true;
     83     int ss=link[v].size();
     84     for(int i=0;i<ss;i++)
     85     {
     86         if(vis[link[v][i].id]==false)
     87         {
     88             son[v].push_back(link[v][i]);
     89             fa[link[v][i].id].id=v;
     90             fa[link[v][i].id].ww=link[v][i].ww;
     91             dfs(link[v][i].id);
     92         }
     93     }
     94 }
     95 void kls()//克鲁斯卡尔预处理
     96 {
     97     sort(edge+1,edge+1+m,cmp);
     98     for(int i=1;i<=n;i++) ff[i]=i;
     99     for(int i=1;i<=m;i++)
    100     {
    101         int f1=father(edge[i].next);
    102         int f2=father(edge[i].to);
    103         if(f1!=f2) 
    104         {
    105             ff[f1]=f2;
    106             sd gg1,gg2;
    107             gg1.id=edge[i].to,gg1.ww=edge[i].w;
    108             gg2.id=edge[i].next,gg2.ww=edge[i].w;
    109             link[edge[i].next].push_back(gg1);
    110             link[edge[i].to].push_back(gg2);
    111         }
    112     }
    113 }
    114 int main()
    115 {
    116     memset(vis,false,sizeof(vis));
    117     scanf("%d%d",&n,&m);
    118     for(int i=1;i<=m;i++)
    119     {
    120         int x,y,w1;
    121         scanf("%d%d%d",&x,&y,&w1);
    122         edge[i].next=x,edge[i].to=y,edge[i].w=w1;
    123     }
    124     kls();
    125     for(int i=1;i<=n;i++)//循环建立树,防止不止一个联通图
    126     {
    127         if(vis[i]) continue;
    128         deep[i]=1; 
    129         dfs(i);
    130         pre(i);
    131     }
    132     scanf("%d",&q);
    133     for(int i=1;i<=q;i++)
    134     {
    135         int a,b;
    136         scanf("%d%d",&a,&b);
    137         if(father(a)!=father(b))//如果不在一个联通图上,那么肯定不能相互到达
    138         {
    139             printf("-1
    ");
    140             continue;
    141         }
    142         printf("%d
    ",lca(a,b));
    143     }
    144     return 0;
    145 }
  • 相关阅读:
    ThinkPHP 3.2.2 实现持久登录 ( 记住我 )
    Java实现 LeetCode 20 有效的括号
    Java实现 LeetCode 20 有效的括号
    Java实现 LeetCode 19删除链表的倒数第N个节点
    Java实现 LeetCode 19删除链表的倒数第N个节点
    Java实现 LeetCode 19删除链表的倒数第N个节点
    Java实现 LeetCode 18 四数之和
    Java实现 LeetCode 18 四数之和
    Java实现 LeetCode 18 四数之和
    Java实现 LeetCode 17 电话号码的字母组合
  • 原文地址:https://www.cnblogs.com/genius777/p/8660546.html
Copyright © 2011-2022 走看看