CCF 201712-4 行车路线

本文介绍了一种基于最短路径算法的改进方案,用于解决特定场景下寻找最优路径的问题。通过维护到每个点的大路信息和最短路径信息,有效地解决了因连续行走小路导致的疲劳度增加问题。

问题描述
  小明和小芳出去乡村玩,小明负责开车,小芳来导航。
  小芳将可能的道路分为大道和小道。大道比较好走,每走1公里小明会增加1的疲劳度。小道不好走,如果连续走小道,小明的疲劳值会快速增加,连续走s公里小明会增加s2的疲劳度。
  例如:有5个路口,1号路口到2号路口为小道,2号路口到3号路口为小道,3号路口到4号路口为大道,4号路口到5号路口为小道,相邻路口之间的距离都是2公里。如果小明从1号路口到5号路口,则总疲劳值为(2+2)2+2+22=16+2+4=22。
  现在小芳拿到了地图,请帮助她规划一个开车的路线,使得按这个路线开车小明的疲劳度最小。
输入格式
  输入的第一行包含两个整数n, m,分别表示路口的数量和道路的数量。路口由1至n编号,小明需要开车从1号路口到n号路口。
  接下来m行描述道路,每行包含四个整数t, a, b, c,表示一条类型为t,连接a与b两个路口,长度为c公里的双向道路。其中t为0表示大道,t为1表示小道。保证1号路口和n号路口是连通的。
输出格式
  输出一个整数,表示最优路线下小明的疲劳度。
样例输入
6 7
1 1 2 3
1 2 3 2
0 1 3 30
0 3 4 20
0 4 5 30
1 3 5 6
1 5 6 1
样例输出
76
样例说明
  从1走小道到2,再走小道到3,疲劳度为52=25;然后从3走大道经过4到达5,疲劳度为20+30=50;最后从5走小道到6,疲劳度为1。总共为76。
数据规模和约定
  对于30%的评测用例,1 ≤ n ≤ 8,1 ≤ m ≤ 10;
  对于另外20%的评测用例,不存在小道;
  对于另外20%的评测用例,所有的小道不相交;
  对于所有评测用例,1 ≤ n ≤ 500,1 ≤ m ≤ 105,1 ≤ a, b ≤ n,t是0或1,c ≤ 105。保证答案不超过106。

解析

这道题目是一道最短路径的改编,需要维护到一点的上一跳是大路的信息和到该路的最短路径信息,因为对于一个点可能由于之前走的是小路而使得到邻居节点的路径不是到该点的路径加上该点到邻居的路径,而是通过大路。所以需要多维护一个上一跳是大路的信息,以下代码使用bellman-ford,可以求得正确结果,但是会超时.
对于Dijkstra思路一样,但是只有80分就超时了。

import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt(),m = scanner.nextInt();
        Map<Integer,List<Edge>> graph = new HashMap<>();
        for (int i = 0;i < m;i ++){
            Edge e1 = new Edge(scanner.nextInt() == 0 ,scanner.nextInt(),scanner.nextInt(),scanner.nextInt());
            List<Edge> neighbor = graph.containsKey(e1.getFrom()) ? graph.get(e1.getFrom()) : new ArrayList<>() ;
            neighbor.add(e1);
            graph.put(e1.getFrom(),neighbor);
            Edge e2 = new Edge(e1.isAvenue(),e1.getTo(),e1.getFrom(),e1.getWeight());
            neighbor = graph.containsKey(e2.getFrom()) ? graph.get(e2.getFrom()) : new ArrayList<>() ;
            neighbor.add(e2);
            graph.put(e2.getFrom(),neighbor);
        }
        /*
        dis[i][0] the last hop is avenue
        dis[i][0] the last hop is avenue
        dis[i][1] the last hop is trail
        dis[i][1] if the last hop is avenue, it is 0, otherwise ,it is the length of continuous trail
         */
        int[][] dis = new int[n+1][3];
        for(int i = 0;i <= n;i ++){
            Arrays.fill(dis[i],10000000);
        }
        dis[1][0] = dis[1][1] = dis[1][2] = 0;
        PriorityQueue<Tuple> queue = new PriorityQueue<>(new Comparator<Tuple>() {
            @Override
            public int compare(Tuple o1, Tuple o2) {
                return (int)(o1.getWeight() - o2.getWeight());
            }
        });
        queue.add(new Tuple(1,0));
        Tuple t;
        while (!queue.isEmpty()){
            t = queue.poll();
            if (t.getWeight() > Math.max(dis[t.getVertext()][0],dis[t.getVertext()][1])){
                continue;
            }
            if (!graph.containsKey(t.getVertext())){
                continue;
            }
            List<Edge> neighbors = graph.get(t.getVertext());
            for (Edge edge : neighbors){
                if (edge.isAvenue()){
                    if (dis[edge.getTo()][0] > dis[edge.getFrom()][1] + edge.getWeight())
                    {
                        dis[edge.getTo()][0] = dis[edge.getFrom()][1] + edge.getWeight();
                        if (dis[edge.getTo()][1] > dis[edge.getTo()][0]){
                            dis[edge.getTo()][1] = dis[edge.getTo()][0];
                            dis[edge.getTo()][2] = 0;
                        }
                        queue.add(new Tuple(edge.getTo(),dis[edge.getTo()][0]));
                    }

                }
                else {

                    if (dis[edge.getTo()][1] > dis[edge.getFrom()][1] - dis[edge.getFrom()][2] * dis[edge.getFrom()][2]
                            + (dis[edge.getFrom()][2] + edge.getWeight()) * (dis[edge.getFrom()][2] + edge.getWeight())){
                        dis[edge.getTo()][2] = edge.getWeight() + dis[edge.getFrom()][2];
                        dis[edge.getTo()][1] = dis[edge.getFrom()][1] - dis[edge.getFrom()][2] * dis[edge.getFrom()][2]
                                + (dis[edge.getTo()][2] * dis[edge.getTo()][2]);
                        queue.add(new Tuple(edge.getTo(),dis[edge.getTo()][1]));
                    }
                    if (dis[edge.getTo()][1] > dis[edge.getFrom()][0] + edge.getWeight() * edge.getWeight()){
                        dis[edge.getTo()][1] = dis[edge.getFrom()][0] + edge.getWeight() * edge.getWeight();
                        dis[edge.getTo()][2] = edge.getWeight();
                        queue.add(new Tuple(edge.getTo(),dis[edge.getTo()][1]));
                    }
                }
            }
        }
        System.out.println(dis[n][1]);
    }

}
class Edge{
    private int from,to,weight;
    private boolean avenue;

    public Edge( boolean avenue,int from, int to, int weight) {
        this.from = from;
        this.to = to;
        this.weight = weight;
        this.avenue = avenue;
    }

    public int getFrom() {
        return from;
    }

    public int getTo() {
        return to;
    }

    public int getWeight() {
        return weight;
    }

    public boolean isAvenue() {
        return avenue;
    }
}
class Tuple{
    private int vertex;
    public int weight;
    Tuple(int vertex,int weight){
        this.vertex = vertex;
        this.weight = weight;
    }
    public int getVertext(){
        return this.vertex;
    }
    public int getWeight(){
        return this.weight;
    }
}

Java代码

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Scanner;

public class Route {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt(),m = scanner.nextInt();
        ArrayList<Edge> directedEdges = new ArrayList<>();
        for(int i = 0;i < m;i ++){
            directedEdges.add(new Edge(scanner.nextInt() == 0 ,scanner.nextInt(),scanner.nextInt(),scanner.nextInt()));
        }
        /*
        dis[i][0] the last hop is avenue
        dis[i][1] the last hop is trail
        dis[i][1] if the last hop is avenue, it is 0, otherwise ,it is the length of continuous trail
         */
        int[][] dis = new int[n+1][3];
        for(int i = 0;i <= n;i ++){
            Arrays.fill(dis[i],10000000);
        }
        dis[1][0] = dis[1][1] = dis[1][2] = 0;
        int temp;
        boolean flag = true;
        for(int i = 0;i < n;i ++){
            flag = true;
            for(Edge edge : directedEdges){
                /*if this edge is avenue, then can update dis[v][0] to dis[v][0]*/
                if(edge.isAvenue()){
                    dis[edge.getTo()][0] = Math.min(dis[edge.getFrom()][1] + edge.getWeight(),dis[edge.getTo()][0]);
                    if(dis[edge.getTo()][1] >= dis[edge.getTo()][0]){
                        dis[edge.getTo()][1] = dis[edge.getTo()][0];
                        dis[edge.getTo()][2] = 0;
                        flag = false;
                    }
                }
                else{
                    temp = dis[edge.getFrom()][1] - dis[edge.getFrom()][2] * dis[edge.getFrom()][2] + (dis[edge.getFrom()][2] + edge.getWeight()) * (dis[edge.getFrom()][2] + edge.getWeight());
                    if (dis[edge.getTo()][1] > temp){
                        dis[edge.getTo()][1] = temp;
                        dis[edge.getTo()][2] = dis[edge.getFrom()][2] + edge.getWeight();
                        flag = false;
                    }
                    temp = dis[edge.getFrom()][0] + edge.getWeight() * edge.getWeight();
                    if (dis[edge.getTo()][1] > temp){
                        dis[edge.getTo()][1] = temp;
                        dis[edge.getTo()][2] = edge.getWeight();
                        flag = false;
                    }
                }
            }
            if(flag){
                break;
            }
        }
        int i = 1;
        System.out.println(dis[n][1]);
    }

}

class Edge{
    private int from,to,weight;
    private boolean avenue;

    public Edge( boolean avenue,int from, int to, int weight) {
        this.from = from;
        this.to = to;
        this.weight = weight;
        this.avenue = avenue;
    }

    public int getFrom() {
        return from;
    }

    public int getTo() {
        return to;
    }

    public int getWeight() {
        return weight;
    }

    public boolean isAvenue() {
        return avenue;
    }
}

C++代码

#include <stdio.h>
#include <cstring>
#include <algorithm>
using namespace std;
int main(){
    int n,m;
    scanf("%d%d",&n,&m);
    int avenue[m*4],from[m*4],to[m*4],weight[m*4];
    for(int i = 0;i < 2*m;i ++){
        scanf("%d%d%d%d",&avenue[i],&from[i],&to[i],&weight[i]);
        avenue[i+1] = avenue[i];
        from[i+1] = to[i];
        to[i+1] = from[i];
        weight[i+1] = weight[i];
        i ++;
    }
    long long dis[n+1][3];
    for(int i = 0;i <= n;i ++){
        for(int j = 0;j < 3;j ++){
            dis[i][j] = 10000000;
        }
    }
    long long temp;
    int flag = 1;
    dis[1][0] = dis[1][1] = dis[1][2] = 0;
    for(int i = 0;i < n;i ++){
        flag = 1;
            for(int j = 0;j < 2*m;j ++){
                /*if this edge is avenue, then can update dis[v][0] to dis[v][0]*/
                if(!avenue[j]){
                    dis[to[j]][0] = min(dis[from[j]][1] + weight[j],dis[to[j]][0] + 0);
                    if(dis[to[j]][1] >= dis[to[j]][0]){
                        dis[to[j]][1] = dis[to[j]][0];
                        dis[to[j]][2] = 0;
                        flag = 0;
                    }
                }
                else{
                    temp = dis[from[j]][1] - dis[from[j]][2] * dis[from[j]][2] + (dis[from[j]][2] + weight[j]) * (dis[from[j]][2] + weight[j]);
                    if (dis[to[j]][1] > temp){
                        dis[to[j]][1] = temp;
                        dis[to[j]][2] = dis[from[j]][2] + weight[j];
                        flag = 0;
                    }
                    temp = dis[from[j]][0] + weight[j] * weight[j];
                    if (dis[to[j]][1] > temp){
                        dis[to[j]][1] = temp;
                        dis[to[j]][2] = weight[j];
                        flag = 0;
                    }
                }
            }
            if(flag){
                break;
            }
    }
    printf("%lld",dis[n][1]);
    return 0;
}
CCF 201712-5 题目“商路”中,问题的核心是动态规划与图论的结合应用。题目大意为:给定一个有向无环图(DAG),每条边有两个属性——利润和成本。商人从某个起点出发,通过选择路径到达终点,在此过程中需要满足资金不为负数的前提下,最大化最终获得的利润。 ### 解法分析 - **动态规划思路**:由于图是有向无环的,因此可以采用拓扑排序来处理节点顺序,并按照拓扑序进行动态规划。 - **状态表示**:设 `dp[u]` 表示到达节点 `u` 时能够获得的最大利润。初始时,起点的利润为 0,其他点初始化为 -∞。 - **转移方程**:对于每个节点 `u`,遍历其所有入边,如果当前的资金加上该边的利润减去成本后仍大于等于 0,则更新目标节点的状态值。 - **实现细节**:通常会使用自底向上的拓扑排序方法来进行 DP 过程,确保每次计算 `dp[v]` 的时候,所有前驱节点 `u` 的状态已经计算完成[^1]。 ### 代码实现示例 以下是一个基于 C++ 的简要实现框架: ```cpp #include <bits/stdc++.h> using namespace std; const int INF = 0x3f3f3f3f; struct Edge { int u, v, profit, cost; }; vector<Edge> edges; vector<int> adj[100005]; int inDegree[100005], dp[100005]; void solve() { int n, m; cin >> n >> m; for (int i = 0; i < m; ++i) { int u, v, p, c; cin >> u >> v >> p >> c; edges.push_back({u, v, p, c}); adj[u].push_back(i); inDegree[v]++; } queue<int> q; for (int i = 1; i <= n; ++i) if (inDegree[i] == 0) q.push(i); while (!q.empty()) { int u = q.front(); q.pop(); for (int idx : adj[u]) { int v = edges[idx].v; int newCost = dp[u] - edges[idx].cost + edges[idx].profit; if (newCost >= 0 && dp[v] < newCost) dp[v] = newCost; inDegree[v]--; if (inDegree[v] == 0) q.push(v); } } cout << *max_element(dp + 1, dp + n + 1) << endl; } ``` 上述代码首先构建了图的邻接表和入度数组,然后执行拓扑排序并在此过程中更新每个节点的最大利润值。需要注意的是,只有当经过某条边后的资金非负时才允许更新后续节点的状态。 ### 复杂度分析 - 时间复杂度:O(N + M),其中 N 是节点数,M 是边数,因为每个节点和边仅被处理一次。 - 空间复杂度:O(N + M),用于存储图结构和动态规划数组。 这种解法能够在保证正确性的前提下高效解决问题,尤其适用于大规模数据输入的情况。 ---
评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值