Educational Codeforces Round 76 (Rated for Div. 2) - E. The Contest(dp)

题意:有三个人去写编号从$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\n", min(dp[n][1], min(dp[n][2], dp[n][3])));
    return 0;
}

 

 

 

 

posted on 2019-11-21 14:04  啊啊鄂  阅读(108)  评论(0编辑  收藏  举报

导航