【USACO】2006 Nov Roadblocks 次短路径

6 篇文章 0 订阅

Roadblocks 次短路径


  • Description

贝西所在的牧场一共有 N 个地点。M 条双向通行的道路连接这些地点,其中第i条道路连接 Ai 和 Bi ,长度为 Li 。贝西想从第一个地点走到第 N 个地点,由于路上风景不错,她决定不走最短路径,而选择次短路径。次短路径的长度严格大于最短路径。如果有两条路径的长度都是最短的,那么它们都不算次短路径。次短路径允许重复通过一些道路或地点。请你帮助贝西找出次短路径的长度吧,输入数据保证次短路径一定存在。

  • Input Format

第一行:两个整数 N 和 M ,1 ≤ N ≤ 5000 , 1 ≤ M ≤ 10^5
第二行到第 M+1 行:第 i+1 行有三个整数 Ai ,Bi 和 Li ,1 ≤Ai ,Bi ≤ N , 1 ≤ Li ≤5000

  • Output Format

单个整数:表示从第一个点到最后一个点的次短路径长度

  • Sample Input

4 4
1 2 100
2 4 200
2 3 250
3 4 100

  • Sample Output

450

  • Hint

最短路是 1 → 2 → 4,总长度为300,次短路是 1 → 2 → 3 → 4,总长度为 450


  • 分析

一题Spfa裸体,无非就是多记录一下每到个点的次短路,然后松弛的时候分别用次短路和最短路更新。要注意的是题意说次短路严格大于最短路,所以在更新次短路是要判断是否等于最短路。


#include <queue>
#include <stack>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
queue <int> Q;
int n,m,u,v,w,tot,In[5005],Dist[5005][2],last[5005];
struct Data{int to,next,val;}E[200005];
void Addline(int u,int v,int w){
    E[++tot].to=v; E[tot].next=last[u]; last[u]=tot; E[tot].val=w;
    E[++tot].to=u; E[tot].next=last[v]; last[v]=tot; E[tot].val=w;
}
int main(){
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
    scanf("%d%d",&n,&m);
    for (int i=1;i<=m;i++){
        scanf("%d%d%d",&u,&v,&w);
        Addline(u,v,w);
    }
    memset(Dist,127/2,sizeof(Dist));
    for (Q.push(1),Dist[1][0]=0;!Q.empty();){
        u=Q.front(); In[u]=0; Q.pop();
        for (int i=last[u];i;i=E[i].next){
            v=E[i].to;
            if (Dist[u][1]+E[i].val<Dist[v][1] && Dist[u][1]+E[i].val!=Dist[v][0]){
                Dist[v][1]=Dist[u][1]+E[i].val;
                if (!In[v]) Q.push(v),In[v]=1;
            }
            if (Dist[v][1]<Dist[v][0]) swap(Dist[v][0],Dist[v][1]);
            if (Dist[u][0]+E[i].val<Dist[v][1] && Dist[u][0]+E[i].val!=Dist[v][0]){
                Dist[v][1]=Dist[u][0]+E[i].val;
                if (!In[v]) Q.push(v),In[v]=1;
            }
            if (Dist[v][1]<Dist[v][0]) swap(Dist[v][0],Dist[v][1]);
        }
    }
    printf("%d",Dist[n][1]);
    fclose(stdin); fclose(stdout);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: p109 [noip2004 提高组] 合并果子: 这道题目是一道经典的贪心算法题目,题目大意是给定n个果子,每个果子的重量为wi,现在需要将这n个果子合并成一个果子,每合并需要消耗的代价为合并的两个果子的重量之和,求最小的代价。 我们可以使用贪心算法来解决这个问题,每选择两个最小的果子进行合并,然后将合并后的果子的重量加入到集合中,重复这个过程直到只剩下一个果子为止。 这个算法的正确性可以通过反证法来证明,假设存在一种更优的合并方案,那么这个方案一定会在某一步将两个比当前选择的两个更小的果子进行合并,这样就会得到一个更小的代价,与当前选择的方案矛盾。 usaco06nov fence repair: 这道题目是一道经典的贪心算法题目,题目大意是给定n个木板,每个木板的长度为li,现在需要将这n个木板拼接成一块长度为L的木板,每拼接需要消耗的代价为拼接的两个木板的长度之和,求最小的代价。 我们可以使用贪心算法来解决这个问题,每选择两个最小的木板进行拼接,然后将拼接后的木板的长度加入到集合中,重复这个过程直到只剩下一个木板为止。 这个算法的正确性可以通过反证法来证明,假设存在一种更优的拼接方案,那么这个方案一定会在某一步将两个比当前选择的两个更小的木板进行拼接,这样就会得到一个更小的代价,与当前选择的方案矛盾。 ### 回答2: 题目描述: 有n个果子需要合并,合并任意两个果子需要的代价为这两个果子的重量之和。现在有一台合并机器,可以将两个果子合并成一堆并计算代价。问将n个果子合并成一堆的最小代价。 这个问题可以用贪心算法来解决,我们可以使用一个最小堆来存储所有果子的重量。每从最小堆中取出两个最小的果子,将它们合并成为一堆,并将代价加入答案中,将新堆的重量加入最小堆中。重复以上步骤,直到最小堆中只剩下一堆为止。这样得到的代价就是最小的。 证明如下: 假设最小堆中的果子按照重量从小到大依为a1, a2, ..., an。我们按照贪心策略,每都将重量最小的两个果子合并成为一堆,设合并的过程为b1, b2, ..., bn-1。因此,可以发现,序列b1, b2, ..., bn-1必然是一个前缀和为a1, a2, ..., an的 Huffman 树变形。根据哈夫曼树的定义,这个树必然是最优的,能够得到的代价最小。 因此,使用贪心策略得到的答案必然是最优的,而且时间复杂度为O(n log n)。 对于[usaco06nov] fence repair g这道题,其实也可以用相同的思路来解决。将所有木板的长度存储在一个最小堆中,每取出最小的两个木板长度进行合并,代价即为这两个木板的长度之和,并将合并后木板的长度加入最小堆中。重复以上步骤,直到最小堆中只剩下一块木板。得到的代价就是最小的。 因此,贪心算法是解决这类问题的一种高效、简单但有效的方法,可以应用于很多有贪心性质的问题中。 ### 回答3: 这两个题目都需要对操作进行模拟。 首先是合并果子。这个题目先将所有果子放进一个优先队列中。每取出来两个果子进行合并,直到只剩下一个果子即为答案。合并的代价为两个果子重量之和。每合并完之后再将新的果子放入优先队列中,重复上述过程即可。 再来看fence repair。这个题目需要用到贪心和并查集的思想。首先将所有板子的长度放入一个最小堆中,每取出堆顶元素即为最的板子,将其与其相邻的板子进行合并,合并的长度为这两块板子的长度之和。操作完之后再将新的板子长度放入最小堆中,重复上述过程直到只剩下一块板子。 关于合并操作,可以使用并查集来实现。维护每个板子所在的集合,每操作时合并两个集合即可。 最后,需要注意的是题目中给出的整数都很大,需要使用long long来存储避免溢出。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值