• Educational Codeforces Round 76 (Rated for Div. 2)


    题意:有三个人去写编号从$1$到$n$的$n$个题目,现在这三个人分别有$k_1$,$k_2$,$k_3$个题目($k_1+k_2+k_3=n$),每次操作你可以将一个人的某一个题目给另一个人,问你最少经过多少次操作使得第一个人写这$n$个题目的前缀,第三个人写这$n$个题目的后缀,第二个人写其他部分,可以有人不写题目。

    思路:设$dp[i][j]$表示把第$i$个题目给第$j$个人最少的次数

    当第$i$个题目本来就属于第一个人时,分析如下:

    • 当把第$i$个题目给第一个人时,显然$dp[i][1]=dp[i-1][1]$,不用进行操作
    • 当把第$i$个题目给第二个人时,因为每个人最后的序列都是连续单调递增的,所以$dp[i][2]$只能从$dp[i-1][1]$和$dp[i-1][2]$转移过来,这两种情况都需要进行一次操作,即$dp[i][2] = min(dp[i - 1][1] + 1, dp[i - 1][2] + 1)$
    • 当把第$i$个题目给第三个人时,同理得$dp[i][3] = min(dp[i - 1][1] + 1, min(dp[i - 1][2] + 1, dp[i - 1][3] + 1))$

    当第$i$个题目本来就属于第二个人或者第三个人时,分析是一样的。

    设$a[x]=i$表示第$x$个题目本来就属于第$i$个人,所以状态转移方程如下:

    for (int i = 1; i <= n; i++) {
        if (1 == a[i]) {
            dp[i][1] = dp[i - 1][1];
            dp[i][2] = min(dp[i - 1][1] + 1, dp[i - 1][2] + 1);
            dp[i][3] = min(dp[i - 1][1] + 1, min(dp[i - 1][2] + 1, dp[i - 1][3] + 1));
        }
        else if (2 == a[i]) {
            dp[i][1] = dp[i - 1][1] + 1;
            dp[i][2] = min(dp[i - 1][1], dp[i - 1][2]);
            dp[i][3] = min(dp[i - 1][1] + 1, min(dp[i - 1][2] + 1, dp[i - 1][3] + 1));
        }
        else {
            dp[i][1] = dp[i - 1][1] + 1;
            dp[i][2] = min(dp[i - 1][1] + 1, dp[i - 1][2] + 1);
            dp[i][3] = min(dp[i - 1][1], min(dp[i - 1][2], dp[i - 1][3]));
        }
    }

    写成两个$for$循环感觉更简洁,但上面的状态转移方程更容易理解。

    #include <iostream>
    #include <algorithm>
    #include <cstdio>
    #include <cstring>
    
    using namespace std;
    
    const int N = 200010;
    const int M = 5;
    const int INF = 0x3f3f3f3f;
    
    int k[M], a[N], dp[N][M];
    
    int main()
    {
        scanf("%d%d%d", &k[1], &k[2], &k[3]);
        for (int i = 1; i <= 3; i++) {
            for (int j = 1; j <= k[i]; j++) {
                int x;
                scanf("%d", &x);
                a[x] = i;
            }
        }
        int n = k[1] + k[2] + k[3];
        memset(dp, INF, sizeof(dp));
        dp[0][1] = dp[0][2] = dp[0][3] = 0;
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= 3; j++) {
                for (int k = 1; k <= j; k++) {
                    dp[i][j] = min(dp[i][j], dp[i - 1][k] + (a[i] != j));
                }
            }
        }
        printf("%d
    ", min(dp[n][1], min(dp[n][2], dp[n][3])));
        return 0;
    }

  • 相关阅读:
    c++ range库
    差分数组
    简单比较一下C++中的引用和指针
    数字图像处理中一张常用图片
    内存池与内存块
    SmartPtr
    Allocator
    Java学习第12天
    Java学习第11天
    Java学习第10天
  • 原文地址:https://www.cnblogs.com/zzzzzzy/p/11904910.html
Copyright © 2020-2023  润新知