洛谷1967 货车运输

to神犇:请自动跳过前x行的吐槽,因为吐槽内容可能只是一些极其无聊的bug和友情提示……

一道极其经典且相当BT的图论题,不过目前看来与运输计划相比似乎更仁慈一些。在你下定决心AC这道题之前,你首先要做好debug到怀疑人生的准备,最好准备一瓶眼药水以备不测,当然有一份画风清奇简约养眼的题解也是极好的……;其次,你要明白这道题的代码长度——不压缩180左右——所以要有足够的耐心,找一把舒适的椅子再好不过了;last but not the least,你要拥有足够的知识储备——图论+搜索=最小生成树+LCA(倍增或tarjan)+RMQ+DFS,建议是别抄模板(当心wa惨)。如果你确信已经具备了以上条件,请点击以下链接或继续阅读,完成AC之路的第一步——审题。友情提示:codevs上的数据略水(水了8个点……),洛谷数据和官方一致。

原题地址:https://www.luogu.org/problem/show?pid=1967

题目描述

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

输入输出格式

输入格式:

输入文件名为 truck.in

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

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

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

接下来 q 行,每行两个整数 xy,之间用一个空格隔开,表示一辆货车需要从 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,0000 < m < 10,0000 < q< 1,000对于 60%的数据,0 < n < 1,0000 < m < 50,0000 < q< 1,000对于 100%的数据,0 < n < 10,0000 < m < 50,0000 < q< 30,0000 ≤ z ≤ 100,000

point.1 建树

显然给出的数据只能构造出一张无向图,可能有重边,可能不连通。直接从这张图上跑无法达到我们的目的,所以要跑一遍最生成树,生成多棵无根树构成森林。实现时要另开一个邻接表存生成的树,双向边连接。另外要注意跑kruscal的时候get两个端点的祖先时注意进行路径压缩,防止超时。

point.2 DFS

枚举节点,理论上同一棵树上的每个节点都可以作为根,所以只要碰到没有搜过的点就来一把dfs,搜过的点都打上标记,这样可以遍历整个森林。dfs的目的有两个:

1.确定每一棵树中节点的父子关系,同时维护f[i][0](从第i个节点向上跳2^0步到达的节点,实际上就是father[i])和minp[i][0](从第i个节点向上跳2^0步走过的边中的最短边);

2. 遍历整棵树,求出每个节点的深度d[i]。

point.3 RMQLCA的初始化

具体算法就不啰嗦了,本质上和模板差不多,只不过这里是树上维护RMQ,写法上和区间RMQ不太一样(其实更好写了)。

point.4 LCA同时维护答案

注意目的不在找到LCA,而是在找的过程中维护最小值,返回值也是这个最小值。

解题思路

都在上文里提到了,这里讲几个debug时碰到的蠢毙了的bug:

1. kruscal路径压缩时,fa[x]=get(fa[x])要放在return之前,否则会导致这一子程序变成回溯过程。结果……T了8个点够不够狠?

2. 最大生成树,所以要重载 < 。还有建边时要建双·向·边。

3. dfs时要先枚举而不能只从1开始搜……好吧这其实并不算bug……

4. 倍增初始化第二维最好至少要跑到15,没错真的有2^10+的深度……

5. 读入一对x/y后,先判断是否在同一棵树上,不在直接输出“-1”,否则跑LCA。

6. 数组要开够要开够要开够……不要忘了边数是m*2。

参考代码

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
int num=0, num2=0,ans,mi,n,m;
int v[10005], v2[10005] ,fa[10005],d[10005],pre_len[10005],pre_num[10005];
int minp[10005][20], f[10005][20];
bool vis[10005];
struct mc
{
     intx,y,l,ne;
     booloperator <(const mc b) const
     {
         returnl>b.l;
     }
}e[100005],p[100005];
 
void put(int x,int y,int l)
{
     num++;
     e[num].x=x;
     e[num].y=y;
     e[num].l=l;
     e[num].ne=v[x];
     v[x]=num;
}
 
int get(int x)
{
     if(fa[x]==x) return x;
     else
     {
         fa[x]=get(fa[x]);
         returnget(fa[x]);
     }
}
 
void put2(int x,int y,int l)
{
     num2++;
     p[num2].x=x;
     p[num2].y=y;
     p[num2].ne=v2[x];
     p[num2].l=l;
     v2[x]=num2;
}
 
void dfs(int x,int fa,int dep)
{
     d[x]=dep;
     f[x][0]=fa;
     vis[x]=1;
     for(int i=v2[x];i;i=p[i].ne)
     {
         inty=p[i].y;
         if(y!=fa)
         {
              minp[y][0]=p[i].l;
              dfs(y,x,dep+1);
         }
     }
}
 
void kru()
{
     inttotal=0;
   sort(e+1,e+num+1);
     for(int i=1;i<=n;i++) fa[i]=i;
     for(int i=1;i<=num;i++)
     {
         intf1=get(e[i].x),f2=get(e[i].y);
         if(f1!=f2)
         {
              fa[f1]=f2;
              put2(e[i].x,e[i].y,e[i].l);
              put2(e[i].y,e[i].x,e[i].l);
              total++;
              if(total==n-1) break;
         }
     }
     memset(vis,0,sizeof(vis));
     for(int i=1;i<=n;i++)
     {
         if(!vis[i])
         {
              vis[i]=1;
              dfs(i,0,1);
         }
     }
}
 
int log_(int n)
{
     intx=1,cnt=0;
     while(x<n)
     {
         x*=2;
         cnt++;
     }
     returncnt;
}
 
void pre()
{
     for(int j=1;j<=15;j++)
     for(int i=1;i<=n;i++)
     {
         f[i][j]=f[f[i][j-1]][j-1];
        minp[i][j]=min(minp[i][j-1],minp[f[i][j-1]][j-1]);
     }
}
 
 
 
int LCA(int x,int y)
{
     intmi=2100000000;
     intd1=d[x],d2=d[y];
     if(d1>d2) swap(x,y);
     intdp=d[y]-d[x];
     if(dp>0)
     {
         for(int i=0;d&&i<=14;i++)
         {
              if(dp&1)
              {
                   mi=min(mi,minp[y][i]);
                   y=f[y][i];
              }
              dp>>=1;
         }
     }
     if(x==y) return mi;
     boolff=1;
     intk=log_(d[x]);
     while(ff)
     {
         if(f[x][0]==f[y][0]) break;
         for(int i=k;i>=0;i--)
         {
              if(f[x][i]!=f[y][i])
              {
                   mi=min(mi,minp[x][i]);
                   mi=min(mi,minp[y][i]);
                   x=f[x][i];
                   y=f[y][i];
                   k=i;
                   break;
              }
         }
     }
     mi=min(mi,minp[x][0]);
     mi=min(mi,minp[y][0]);
     return(mi);
}
 
int main()
{
     ios::sync_with_stdio(false);
     scanf("%d%d",&n,&m);
     intx,y,z;
     for(int i=1;i<=m;i++)
     {
         scanf("%d%d%d",&x,&y,&z);
         put(x,y,z);
     }
     kru();
     pre();
     intp,root;
     scanf("%d",&p);
     for(int i=1;i<=p;i++)
     {
         scanf("%d%d",&x,&y);
         intk1=get(x),k2=get(y);
         if(k1!=k2)
         {
              printf("%d\n",-1);
              continue;
         }
         ans=LCA(x,y);
         printf("%d\n",ans);
     }
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值