动态规划优化问题-2

例一:墙壁涂色问题:

​ 共有n面墙壁围成一圈,共有k种颜色,相邻墙壁不能涂同一种颜色,问共有多少种涂色方案?

​ 我们可以想到利用dp[i][j]代表第i面墙涂第j种颜色的方案数,初始化dp[1][1] = 1,利用递推公式dp[i][j] += dp[i - 1][k] (其中k != j),最后ans += dp[n][k] (其中k != 1)ans *= k 即为答案

​ 按照上面的思路,我们发现它需要开二维数组,空间复杂度为O(n * k),时间复杂度也为O(n * k)

​ 如果题目中n * k的数据范围超过1e7该怎么办呢?

​ 这时,我们就可以通过重新定义状态以去掉那些冗余项来对其进行优化,大家可以先考虑考虑如何进行优化成时间和空间复杂度均为O(n)再接着往下看~

​ 这时,我们可以设dp[i]代表第i面墙与第一面墙成环的方案数,初始化dp[1] = 1, dp[2] = k - 1, dp[3] = (k - 2) * dp[2]

​ 我们可以发现,第i面墙与第一面墙成环,那么需要保证第i面墙与第1面颜色不同,也与第i - 1面墙颜色不同。由于dp[i - 1]代表i - 1面墙成环的方案数,因此i - 1面墙与第一面墙颜色不同,所以当第i - 1面墙与第一面墙颜色不同的情况下,dp[i]dp[i - 1] * (k - 2)种方案。

​ 除此之外,第i - 1面墙也可以与第一面墙相同。这时假若第i - 1面墙与第一面墙颜色相同,那么前i - 1面墙的方案数为dp[i - 2],由于第一面和第i - 1面墙颜色相同,因此在该种情况下第i面墙的方案数为dp[i - 2] * (k - 1)

​ 这两种情况的总和就是dp[i]的方案总数,即当i > 3时:dp[i] = dp[i - 1] * (k - 2) + dp[i - 2] * (k - 1)

​ 最终dp[n] *= k即为答案

​ 给出这道题的解题代码(由于数据范围爆longlong因此用大数):

#include <stdio.h>

int dp[1005][2005] = {0};

int main () {
    int n, k;
    scanf("%d%d", &n, &k);
    dp[1][0] = dp[2][0] = dp[3][0] = dp[1][1] = 1;
    dp[2][1] = k - 1;
    dp[3][1]= (k - 2) * dp[2][1];
    if (dp[3][1] >= 10) {
        dp[3][0]++;
        dp[3][2] = dp[3][1] / 10;
        dp[3][1] %= 10;
    }
    for (int i = 4; i <= n; i++) {
        dp[i][0] = dp[i - 1][0];
        for (int j = 1; j <= dp[i][0]; j++) {
            dp[i][j] = (k - 2) * dp[i - 1][j] + (k - 1) * dp[i - 2][j];
        }
        for (int j = 1; j <= dp[i][0]; j++) {
            if (dp[i][j] >= 10) {
                dp[i][j + 1] += dp[i][j] / 10;
                dp[i][j] %= 10;
            }
        }
        int j = dp[i][0] + 1;
        while(dp[i][j]) {
            dp[i][0]++;
            if (dp[i][j] >= 10) {
                dp[i][j + 1] = dp[i][j] / 10;
                dp[i][j] %= 10;
            }
            j++;
        }
    }
    for (int i = 1; i <= dp[n][0]; i++) {
        dp[n][i] *= k;
    }
    for (int i = 1; i <= dp[n][0]; i++) {
        if(dp[n][i] >= 10) {
            dp[n][i + 1] += dp[n][i] / 10;
            dp[n][i] %= 10;
        }
    }
    if (dp[n][dp[n][0] + 1]) dp[n][0]++;
    for (int i = dp[n][0]; i > 0; i--) {
        printf("%d", dp[n][i]);
    }
    printf("\n");
    return 0;
}

转载请注明出处!!!

如果有写的不对或者不全面的地方 可通过主页的联系方式进行指正,谢谢

猜你喜欢

转载自blog.csdn.net/Ivan_zcy/article/details/86504451
今日推荐