2.18

2.18

P1613 跑路 - 洛谷 (luogu.com.cn)

  • 越来越感觉在做图论了.......
  • 首先本题运用了倍增的思想(特地去学了一下倍增),倍增主要是运用于lca(寻找结点的最近祖先结点)的问题中。简单来说我们维护一个一维线性数组,也就是\(dp[i][j]\)表示我们从i点向右走\(2^j\)个位置,那么我们其实是可以维护出一个转移方程的,也就是\(dp[i][j] = dp[dp[i][j-1]][j-1]\) 也就是我们从i点向右走\(2^j\)个位置,其实就等价于从i点向右走\(2^{j-1}\)后的点再走\(2^{j-1}\),其实就是走了\(2^j\)个点。
  • 那么我们放在这个题可以考虑的是,如果我们从i点到j点能够达到\(2^j\)个距离的话,那么我们的\(dist[i][j] = 1\),那么我们思路就出来了,维护一个\(str[i,j,k]\)数组表示,i点到j点距离为\(2^k\)的路径是否可以直接实用跑路机。
  • 所以我们在初始化时,只需要枚举k,t(中间结点),i,j四个变量,并满足\(str[i][t][k - 1] \&\& str[t][j][k - 1]\)时就满足\(str[i][j][k]=true\),满足我们在i点到j点直接添加一条边
  • ok,那么我们直接对这个图使用一次floyd算法即可,多元点最短路,因为n<=50,所以是非常ok的
import java.io.*;
import java.util.*;
import java.util.zip.ZipEntry;

public class Main implements Runnable {

    static StreamTokenizer cin = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
    static PrintWriter cout = new PrintWriter(new OutputStreamWriter(System.out));
    static final int N = 55;
    static final int M = 32;
    static int[][] dist = new int[N][N];
    static boolean[][][] str = new boolean[N][N][M];//i点到j点是否存在长度为2^k的一条边
    static int n, m;

    public static void main(String[] args) {
        new Thread(null, new Main(), "", 1 << 29).start();
    }

    public static int nextInt() throws IOException {
        cin.nextToken();
        return (int) cin.nval;

    }

    public static void dfs(int parent, int cnt) {

    }

    public static void init() {
        for (int k = 1; k < M; k++) {
            for (int t = 1; t <= n; t++) {
                for (int i = 1; i <= n; i++) {
                    for (int j = 1; j <= n; j++) {
                        if (str[i][t][k - 1] && str[t][j][k - 1]) {
                            str[i][j][k] = true;
                            dist[i][j] = 1;
                        }
                    }
                }
            }
        }
    }

    public static void floyd() {
        for (int k = 1; k <= n; k++) {
            for (int i = 1; i <= n; i++) {
                for (int j = 1; j <= n; j++) {
                    dist[i][j] = Math.min(dist[i][j], dist[i][k] + dist[k][j]);
                }
            }
        }
    }

    @Override
    public void run() {
        try {
            n = nextInt();m = nextInt();
            for (int i = 1; i <= n; i++) {
                Arrays.fill(dist[i], 0x3f3f3f3f);
            }
            for (int i = 1; i <= m; i++) {
                int x = nextInt(); int y = nextInt();
                str[x][y][0] = true;
                dist[x][y] = 1;
            }

            init();
            floyd();
            cout.println(dist[1][n]);
            cout.flush();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }


}

Problem - 148D - Codeforces

  • 属于是给概率dp打了一下基础
  • 求概率是从初始状态转移到结束状态
import java.io.*;
import java.util.*;
import java.util.zip.ZipEntry;

public class Main implements Runnable {

    static StreamTokenizer cin = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
    static PrintWriter cout = new PrintWriter(new OutputStreamWriter(System.out));
    static int w, b;
    static final int N = (int) (1e3 + 10);
    static double[][] dp = new double[N][N];

    public static void main(String[] args) {
        new Thread(null, new Main(), "", 1 << 29).start();
    }

    public static int nextInt() throws IOException {
        cin.nextToken();
        return (int) cin.nval;

    }
    @Override
    public void run() {
        try {
            w = nextInt();b = nextInt();
            for (int i = 1; i <= w; i++) {
                dp[i][0] = 1;
            }
            for (int i = 1; i <= w; i++) {
                for (int j = 1; j <= b; j++) {
                    dp[i][j] += (double) i / (i + j);//先手拿白鼠
                    if (j >= 3) {
                        dp[i][j] += ((double)j / (i + j)) * ((double)(j - 1) / (i + j - 1)) * ((double)(j - 2) / (i + j - 2)) * dp[i][j - 3];
                    }
                    if (i >= 1 && j >= 2) {
                        dp[i][j] += ((double)j / (i + j)) * ((double)(j - 1) / (i + j - 1)) * ((double)(i) / (i + j - 2)) * dp[i - 1][j - 2];
                    }
                }
            }
            cout.println(String.format("%.9f", dp[w][b]));
            cout.flush();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }


}

P4316 绿豆蛙的归宿 - 洛谷 (luogu.com.cn)

  • 与上一道题不一样,本题是求解期望,由上图所示,这是期望的公式,分析公式我们知道就是我们需要求出两个值,也就是概率p和权重x(对于每个概率),要理清x的概念,在不同情况都有不同的运用。比如求解路径长度的期望值,那么我们的权重其实就是这条路径的长度
  • 考虑本题是求解期望,其实末尾状态时较好得出的,也就是我们经典的倒推,也就是从结尾状态得出初始状态。\(dp[i]\)就为当前结点i转为最终状态结点的期望。因为\(dp[n]=0\),也就是最终状态期望为0,很好想到就是n到n点,路径为0,1*0=0。
  • 那么考虑转移公式,假设我们有i点到j点的一个情况,也就是逆推的话就是j点推i点。那么\(dp[i] += (dp[j] + w[i][j]) * p[i][j]\),然后就是如果i点可以到达很多j点(j点为i点的子节点,可以考虑到i点会有很多子节点),全部累加就好。
  • ok,那么看看数据范围$ 1<= n <= 1e5$,显然如果采用二维数组去存的话会爆空间。考虑存边,这里推荐一个写法,直接用Vector存储一个结构体(该边的另一个结点和边权),详细看代码吧,感觉挺好的,目前没有发现弊端
  • 注意就是因为我们倒推,添加方向调转一下就好了,因为题目保证了有向无环图,也就是我们说的DAG,那么考虑拓扑排序即可,复杂度达到\(O(n + m)\),顺利ac
import java.io.*;
import java.util.*;
import java.util.zip.ZipEntry;

public class Main implements Runnable {

    static StreamTokenizer cin = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
    static PrintWriter cout = new PrintWriter(new OutputStreamWriter(System.out));
    static int n, m;
    static final int N = (int) (1e5 + 10);
    static int[] in = new int[N];
    static double[] dp = new double[N];
    static double[] p = new double[N];
    static Queue<Integer> queue = new PriorityQueue<>();
    static Vector<node>[] nodes = new Vector[N];

    public static void main(String[] args) {
        new Thread(null, new Main(), "", 1 << 29).start();
    }

    public static int nextInt() throws IOException {
        cin.nextToken();
        return (int) cin.nval;

    }

    public static void topSort() {
        while (!queue.isEmpty()) {
            Integer now = queue.poll();
            for (node son : nodes[now]) {
                if (--in[son.x] == 0) queue.add(son.x);
                dp[son.x] += (dp[now] + son.val) * p[son.x];
            }
        }
    }

    @Override
    public void run() {
        try {
            n = nextInt();m = nextInt();
            for (int i = 1; i <= n; i++) nodes[i] = new Vector<>();
            for (int i = 1; i <= m; i++) {
                int x = nextInt(); int y = nextInt();int val = nextInt();
                nodes[y].add(new node(x, val));
                in[x]++;
            }

            for (int i = 1; i <= n - 1; i++) {
                p[i] = (1.0 / in[i]);
            }
            queue.add(n);dp[n] = 0;
            topSort();

            cout.println(String.format("%.2f", dp[1]));
            cout.flush();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    static class node {
        int x;
        int val;

        public node(int x, int val) {
            this.x = x;
            this.val = val;
        }
    }

}
posted @ 2025-02-18 19:01  Mikkeykarl  阅读(13)  评论(0)    收藏  举报