入门OJ:扫雪

扫雪1

题目描述
大雪履盖了整个城市,市政府要求冬季服务部门尽快将一些街道(列在一份清单中)的积雪清除掉以恢复交通,整个城市由许多交叉路口和街道构成,当然任意两个交叉路口都是直接或间接连通的,清单给出了最少的街道,使得这些街道的积雪清除后任意两个交叉路口之间有且仅有一条通路,冬季服务部门只有一辆铲雪车及一名司机,这辆铲雪车的出发点位于某个交叉路口。
无论街道上有没有积雪,铲雪车每前进一米都要消耗一升燃料,冬季服务部门要求司机在铲除清单上的所有街道的积雪的前提下,要求消耗燃料最少,积雪铲完后车可以停在任意的交叉路口。
输入格式
输入文件的第一行包含两个整数N和S,1≤N≤100000,1≤S≤N。N为交叉路口的总数;S为铲雪车出发的路口序号。路口的标号为1••N。
接下来的N-1行为清单上的街道,每一行包含三个用空格隔开的整数A、B、C,表示一条从交叉路口A到交叉路口B的街道,C为该街道的长度,单位为米,1≤C≤1000。
输出格式
输出文件仅一行包含一个整数表示清除所有积雪所需的最少的燃料数量。

根据题意,扫雪车在去往一棵子树扫雪之后是还会再回到根的位置。也就是说,子树上的边会被经过两次。而最后的时候扫雪车走到了某个叶子节点是不用再回来的,也就是从根到这个节点的路径上的边只会被经过一次。设所有边权之和为sum,从根节点到最后一个叶子节点的路径长度为len,那么答案就是sum*2-len。其中sum可以在输入时算出,是定值,我们只需让len尽可能大即可。我们可以算出根到所有节点的距离(也可以看成是深度),取最大值即可。复杂度为O(n)。
 
 
 
xxxxxxxxxx
 
 
 
 
#include<iostream>
#include<cstring>
#include<cstdio>
#define maxn 100001
using namespace std;
 
         
struct edge{
    int to,dis,next;
    edge(){}
    edge(const int &_to,const int &_dis,const int &_next){
        to=_to,dis=_dis,next=_next;
    }
}e[maxn<<1];
int head[maxn],k;
inline void add(const int &u,const int &v,const int &w){
    e[k]=edge(v,w,head[u]);
    head[u]=k++;
}
 
         
int dep[maxn],n,s,sum;//dep[i]表示根到i的距离
void dfs(int u,int pre){
    for(register int i=head[u];~i;i=e[i].next){
        int v=e[i].to;
        if(v==pre) continue;
        dfs(v,u);
        dep[u]=max(dep[u],dep[v]+e[i].dis);
    }
}
 
         
int main(){
    memset(head,-1,sizeof head);
    scanf("%d %d",&n,&s);
    for(register int i=1;i<n;i++){
        int u,v,w;
        scanf("%d %d %d",&u,&v,&w);
        add(u,v,w),add(v,u,w);
        sum+=w;
    }
    dfs(s,0);
    printf("%d\n",sum*2-dep[s]);
    return 0;
}
 
 


扫雪2

跟扫雪1唯一的不同是现在有两辆车。

类比着扫雪1的做法,设边权之和为sum,第一辆扫雪车停的叶子节点到根的距离为len1,第二辆的为len2,答案就是sum*2-len1-len2,我们只需让len1+len2尽可能大即可。类似地,我们可以用扫雪1的做法求出len1和len2,再代进答案式子即可。不过这样做要判断len1的路径和len2没有交点,挺麻烦的。不过既然是两条不同的路径接在一起之后要尽可能地大,不难想到len1+len2就是直径。所以我们求出直径即可。dfs和dp求直径皆可,时间复杂度都是O(n)。
 
 
 
xxxxxxxxxx
 
 
#include<iostream>
#include<cstring>
#include<cstdio>
#define maxn 100001
using namespace std;
 
         
struct edge{
    int to,dis,next;
    edge(){}
    edge(const int &_to,const int &_dis,const int &_next){
        to=_to,dis=_dis,next=_next;
    }
}e[maxn<<1];
int head[maxn],k;
inline void add(const int &u,const int &v,const int &w){
    e[k]=edge(v,w,head[u]);
    head[u]=k++;
}
 
         
int f[maxn],len,n,s,sum;
void dp(int u,int pre){
    for(register int i=head[u];~i;i=e[i].next){
        int v=e[i].to;
        if(v==pre) continue;
        dp(v,u);
        len=max(len,f[u]+f[v]+e[i].dis);
        f[u]=max(f[u],f[v]+e[i].dis);
    }
}
 
         
int main(){
    memset(head,-1,sizeof head);
    scanf("%d %d",&n,&s);
    for(register int i=1;i<n;i++){
        int u,v,w;
        scanf("%d %d %d",&u,&v,&w);
        add(u,v,w),add(v,u,w);
        sum+=w;
    }
    dp(1,0);
    printf("%d\n",sum*2-len);
    return 0;
 
 
}
 
 

转载于:https://www.cnblogs.com/akura/p/10804334.html

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值