2.7

P1874 快速求和 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

  • 本题\(dp[i][j]\)代表以i结束和为k的最小加号用量,用\(num[i][j]\)代表i-j范围内的数字
  • 转移方程为\(dp[i][k] = min(dp[j][k - num[j + 1][i]]) + 1\),该方程的意义为再以i结尾和为k的所用加号为,以j结尾和为k减去\([j + 1, i]形成的数字\)
  • 于是我们可以考虑到需要遍历三个要素,i所有数字(字符串长度),k所有和(所给n),j(小于i),复杂度为\(O(len(s)^2 * n)\),大概是\(10^8\),需要考虑优化一下
  • 考虑只要是\(num[j + 1][i] > n\)就不进入,那么理想的话可以降为\(10^7到10^8\),来一发看看情况,ok过了,感觉数据给的easy了点
  • 注意初始化\(dp[0][0] = -1\),方便后面枚举到\(dp[i][num[i][i]] = 0\)这种情况
import java.io.*;
import java.util.*;

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 Scanner cin = new Scanner(System.in);
    static String s = new String();
    static long n;
    static final int N = 45;
    static final int M = (int) (1e5 + 10);
    static long[][] num = new long[N][N];//num[i][j] i-j的数字
    static long[][] dp = new long[N][M];// dp[i][k] 以i结束和为j的最小加号用量

    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() {
        s = cin.next();
        n = cin.nextInt();
        for (int i = 0; i <= 40; i++) Arrays.fill(dp[i], 0x3f3f3f3f);

        for(int i = 1; i <= s.length(); i++) {
            for (int j = i; j <= s.length(); j++) {
                num[i][j] = num[i][j - 1] * 10 + (s.charAt(j - 1) - '0');
            }
        }

        dp[0][0] = -1;
        for (int i = 1; i <= s.length(); i++) {
            for (int k = 0; k <= n; k++) {
                for (int j = i - 1; j >= 0 && num[j + 1][i] <= n; j--) {
                    if (k - num[j + 1][i] < 0) continue;
                    dp[i][k] = Math.min(dp[i][k], dp[j][(int) (k - num[j + 1][i])] + 1);
                }
            }
        }


        long ans = 0x3f3f3f3f;
        if (dp[s.length()][(int) n] < 45) {
            ans = dp[s.length()][(int) n];
        } else {
            ans = -1;
        }

        cout.println(ans);
        cout.flush();
    }


}

P2758 编辑距离 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

  • 考虑\(dp[i][j]\)代表以i的串变为以j结尾的串所需要的最少操作次数。那么一共有四种可能的转移
  • \(a[i] == b[j]时,dp[i][j] = dp[i - 1][j - 1]\),因为不需要操作
  • \(a[i] != b[j]时,dp[i][j] = min(dp[i - 1][j], dp[i][j - 1],dp[i - 1][j - 1]) + 1\),分别对应当子串1先去除末尾字符再转为j的操作数,子串1转为j-1子串再添加一个末尾字符,子串i-1变为j-1最后一个字符由修改操作变为j的末尾字符。
  • 注意初始化即可
import java.io.*;
import java.util.*;

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 Scanner cin = new Scanner(System.in);
    static String s1, s2;
    static final int N = (int) (2e3 + 10);
    static long[][] dp = new long[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() {
        s1 = cin.next();s2 = cin.next();
        for (int i = 1; i<=s1.length(); i++) dp[i][0] = i;
        for (int i = 1; i<=s2.length(); i++) dp[0][i] = i;

        for (int i = 1; i <= s1.length(); i++) {
            for (int j = 1; j <= s2.length(); j++) {
                if (s1.charAt(i - 1) == s2.charAt(j - 1)) {
                    dp[i][j] = dp[i - 1][j - 1];
                } else {
                    dp[i][j] = Math.min(dp[i][j - 1], Math.min(dp[i - 1][j], dp[i - 1][j - 1])) + 1;
                }
            }
        }

        cout.println(dp[s1.length()][s2.length()]);
        cout.flush();
    }


}

P1439 【模板】最长公共子序列 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

  • 传统的做法是\(dp[i][j]\)定义为i,j结尾的两个序列中最长公共子序列的长度,转移方程可以考虑到为,\(当a[i] = b[j]时,dp[i][j] = dp[i - 1][j - 1] + 1 与 a[i] != b[j] 时,dp[i][j] = max(dp[i - 1][j], dp[i][j - 1])\),这无疑我们要枚举i,j,也就是\(O(n^2)\)
  • 然而题目给的\(n <= 10^5\), 我们考虑复杂度降为\(O(nlogn)\)的方法
  • 举个例子 样例中3 2 1 4 5与1 2 3 4 5,我们把串1的序号也排出来\(3(1),2(2),1(3),4(4),5(5)\),可以发现他们的序号是递增的,那么我们的串1可以看作永远是递增的,那么串2就可以发现为\(1(3), 2(2),3(1),4(4),5(5)\),我们发现如果我们能找到序号递增的子序列那么他们一定是串1的子序列。
  • 那么问题就转换成了求单序列的最长递增子序列,用个二分优化一下即可。
import java.io.*;
import java.util.*;

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;
    static final int N = (int) (1e5 + 10);
    static long[] a = new long[N];
    static long[] b = new long[N];
    static long[] target = new long[N];
    static Map<Long, Long>map = new HashMap<>();

    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 {
            n = nextInt();
            for (int i = 1; i <= n; i++) {
                a[i] = nextInt();
                map.put(a[i], (long) i);
            }
            for (int i = 1; i <= n; i++) b[i] = nextInt();

            long len = 0;target[(int) len] = 0;
            for (int i = 1; i <= n; i++) {
                if (target[(int) len] < map.get(b[i])) {
                    target[(int) ++len] = map.get(b[i]);
                    continue;
                }

                long l = 1; long r = len;
                while(l <= r) {
                    long mid = (l + r) / 2;
                    if (target[(int) mid] < map.get(b[i])) {
                        l = mid + 1;
                    } else {
                        r = mid - 1;
                    }
                }
                target[(int) l] = map.get(b[i]);
            }

            cout.println(len);
            cout.flush();


        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }


}
posted @ 2025-02-07 23:00  Mikkeykarl  阅读(15)  评论(0)    收藏  举报