2021-08-14

  • 使用 dp[cnt][p][i] 记录 状态间的转换

  • 状态:使用折扣的次数(2),使用折扣的种类(2)

dp[cnt][][]中的cnt–>0:未使用折扣, 1:使用1次折扣, 2:使用2次折扣
dp[][p][]中的p --> 1:使用p1折扣, 0:使用p2折扣
dp[][][i]中的i --> 节点编号

*注意,((成本cost-折扣p)>0)? (成本cost-折扣p) : 0 ;*
使用折扣一定比不使用更小,最后的答案:Math.min(dp[2][1][N], dp[2][0][N])
  • 状态转换:
1.折扣的次数

dp[0]–>dp[1]–>dp[2]

2.折扣的种类

dp[0][1]==dp[0][0]
dp[0][1]–>dp[1][0]–>dp[2][1]
dp[0][0]–>dp[1][1]–>dp[2][0]

3.状态转换点

在当前节点时使用折扣–> dp[][][to]> dp[][][cur]+(成本cost-折扣p)
当前节点前已经使用折扣–>dp[][][to]> dp[][][cur]+成本cost


import java.io.*;
import java.util.*;

public class Solution {
    
    

    public static void main(String[] args) throws IOException {
    
    
        in = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
        int T = nextInt();
        for (int i = 0; i <= MaxN; i++) adj.add(new ArrayList<>());
        for (int tc = 1; tc <= T; tc++) {
    
    
            readCase();
            work();
            System.out.println("#" + tc + " " + Math.min(dp[2][1][N], dp[2][0][N]));
        }
    }

    static void work() {
    
    
        for (int i = 0; i <= 2; i++) {
    
    
            for (int j = 0; j < 2; j++) {
    
    
                Arrays.fill(dp[i][j], 0, N + 1, INF);
            }
        }
        dp[0][0][1] = dp[0][1][1] = 0;
        dp[1][0][1] = dp[1][1][1] = 0;
        dp[2][0][1] = dp[2][1][1] = 0;
        pq.add(new KV(1, 0, 0, 0, 0));
        pq.add(new KV(1, 0, 1, 0, 0));
        while (!pq.isEmpty()) {
    
    
            KV fr = pq.poll();
            for (int[] to : adj.get(fr.idx)) {
    
    
                if (fr.fa == to[0]) continue;
                //不使用折扣 包括已经使用过折扣
                if (dp[fr.cnt][fr.type][to[0]] > fr.cost + to[1]) {
    
    
                    dp[fr.cnt][fr.type][to[0]] = fr.cost + to[1];
                    pq.add(new KV(to[0], fr.cnt, fr.type, dp[fr.cnt][fr.type][to[0]], fr.idx));
                }
                //在此时使用折扣
                if (fr.cnt < 2) {
    
    
                    int cnt = fr.cnt + 1;
                    int type = (fr.type == 0) ? 1 : 0;
                    int cost = (to[1] > P[type]) ? to[1] - P[type] : 0;
                    if (dp[cnt][type][to[0]] > fr.cost + cost) {
    
    
                        dp[cnt][type][to[0]] = fr.cost + cost;
                        pq.add(new KV(to[0], cnt, type, dp[cnt][type][to[0]], fr.idx));
                    }
                }
            }
        }
        for (int i = 1; i <= N; i++) adj.get(i).clear();
    }

    static void readCase() throws IOException {
    
    
        N = nextInt();
        M = nextInt();
        P[0] = nextInt();
        P[1] = nextInt();
        for (int i = 0; i < M; i++) {
    
    
            int a = nextInt();
            int b = nextInt();
            int c = nextInt();
            adj.get(a).add(new int[]{
    
    b, c});
            adj.get(b).add(new int[]{
    
    a, c});
        }
    }

    static int nextInt() throws IOException {
    
    
        in.nextToken();
        return (int) in.nval;
    }

    static final int MaxN = 40000;
    static final long INF = Long.MAX_VALUE >> 1;
    static final int[] P = new int[2];
    static final long[][][] dp = new long[3][2][MaxN + 2];
    static final ArrayList<ArrayList<int[]>> adj = new ArrayList<>();
    static final PriorityQueue<KV> pq = new PriorityQueue<>(Comparator.comparingLong(KV::getCost));
    static StreamTokenizer in;
    static int N, M;

    static class KV {
    
    
        int idx, cnt, type, fa;
        long cost;

        KV(int i, int c, int p, long d, int f) {
    
    
            idx = i;
            cnt = c;
            type = p;
            cost = d;
            fa = f;
        }

        long getCost() {
    
    
            return cost;
        }
    }
}

猜你喜欢

转载自blog.csdn.net/awp0011/article/details/119697207