【动态规划】独立任务、石子合并

目录

   m处理器


动态规划:

在一般情况下,动态规划通常包括以下几个步骤来实现:

  1. 定义子问题:确定问题的子问题,并将原问题划分为子问题。子问题应该是原问题的较小规模的版本。

  2. 定义状态:确定表示子问题的状态。状态是问题的关键属性,它们的值随着问题的求解而改变。

  3. 定义递推关系:确定子问题之间的递推关系,即如何从较小规模的子问题推导出较大规模的子问题的解。这通常是通过分析问题的特征和限制条件来得到的。

  4. 确定边界条件:确定最小规模的子问题的解,也就是边界条件。边界条件是递归或迭代过程中终止条件的基础。

  5. 确定计算顺序:确定计算子问题的顺序。有些子问题可能依赖于其他子问题的解,因此需要按照一定的顺序进行计算,确保所依赖的子问题已经求解。

  6. 填充表格或数组:使用递推关系和边界条件,填充表格或数组来保存子问题的解。这样可以避免重复计算子问题,提高效率。

  7. 求解原问题:根据填充的表格或数组,从子问题的最优解中得出原问题的解。

  8. 可选的优化步骤:根据具体问题的特点,可以进行一些优化操作,如空间优化、状态压缩等,以减少内存消耗或提高算法效率。

独立任务最优调度问题

#include <stdio.h>
#include <stdlib.h>

#define MAXTIME 500

int p[MAXTIME][MAXTIME];

int Dynamic(int a[], int b[], int n) {
    int aTime = 0; // 作业仅在机器a上运行所需的时间
    for (int k = 1; k <= n; k++) { // 从1开始,到n,方便操作
        aTime += a[k]; // 记录此时a机器单独操作需要的最大时间
        for (int t = 0; t <= aTime; t++) { // 一列一列地更新
            if (t < a[k]) { // 此时时间条件不允许a单独处理k
                p[t][k] = p[t][k - 1] + b[k]; // 此处p元素值直接是b处理完k-1后继续处理k
            } else { // 此时时间条件允许a处理新加入的k,进行比较
                p[t][k] = p[t - a[k]][k - 1] > p[t][k - 1] + b[k] ? p[t][k - 1] + b[k] : p[t - a[k]][k - 1];
            }
        }
    }
    int minTime = 999999;
    for (int t = 0; t < aTime; t++) {
        int maxt = t > p[t][n] ? t : p[t][n]; // 找到每个时间段对应的两者的最大值
        minTime = minTime > maxt ? maxt : minTime; // minTime每次更新,记录最小值
    } // 说白了就是横坐标t就是a单独处理a的用时,p内值就是a、b并行工作后b的最优用时
    // 在考虑到所有作业时,比较最后一列每一行两者的大小就能得到最优用时
    return minTime;
}

int main() {
    int n; // n个作业
    scanf("%d", &n); // 读入作业个数
    int a[n + 1];
    int b[n + 1];
    for (int i = 1; i <= n; i++) // 读入机器A运行时间
        scanf("%d", &a[i]);
    for (int i = 1; i <= n; i++) // 读入机器B运行时间
        scanf("%d", &b[i]);
    int minTime = Dynamic(a, b, n);
    printf("%d", minTime);
    return 0;
}

在这段代码中,递推式和边界条件如下:

递推式:

  1. for (int k = 1; k <= n; k++) {
        aTime += a[k];
        for (int t = 0; t <= aTime; t++) {
            if (t < a[k]) {
                p[t][k] = p[t][k - 1] + b[k];
            } else {
                p[t][k] = p[t - a[k]][k - 1] > p[t][k - 1] + b[k] ? p[t][k - 1] + b[k] : p[t - a[k]][k - 1];
            }
        }
    }

边界条件:

int minTime = 999999;
for (int t = 0; t < aTime; t++) {
    int maxt = t > p[t][n] ? t : p[t][n];
    minTime = minTime > maxt ? maxt : minTime;
}

具体思想

  1. 定义问题:这段代码解决的问题是将n个作业分配到两台机器(机器A和机器B)上完成,并使得完成所有作业所需的时间最小化。

  2. 确定状态:在这个问题中,状态可以用二维数组p[t][k]表示。其中,p[t][k]表示在时间为t且前k个作业已被分配的情况下,机器B完成作业所需的最小时间。

  3. 定义递推关系:这段代码使用了递推关系来计算最优解。它通过迭代遍历作业和时间来更新数组p的值。具体的递推关系如下:

    • t < a[k]时,表示此时时间条件不允许机器A单独处理作业k。因此,递推式为p[t][k] = p[t][k-1] + b[k]
    • t >= a[k]时,表示时间条件允许机器A处理作业k。此时,递推式为p[t][k] = min(p[t-a[k]][k-1], p[t][k-1] + b[k])
  4. 确定边界条件:在初始化阶段,将p[0][k]的所有元素设置为0,表示在时间为0时,无论有多少个作业,机器B都不需要运行。将p[t][0]的所有元素设置为0,表示在没有作业时,无论时间是多少,机器B都不需要运行。

  5. 填充表格:通过使用递推关系和边界条件,遍历作业和时间,填充二维数组p。每个格子p[t][k]的值表示在时间t和前k个作业已被分配的情况下,机器B完成作业所需的最小时间。

  6. 求解原问题:在考虑所有作业时,比较最后一列每一行的两个值,即p[t][n]t,取较大值作为机器B的最优用时。然后通过遍历所有时间段的最优用时,找到最小值作为整体的最优用时。

石子合并问题

#include<stdio.h>
#include<string.h>
#define Ma_x 99999
#define Mi_x 0
#define min(a,b) a<b?a:b
#define max(a,b) a>b?a:b

int n,w[200],dp[200][200],dq[200][200];//n表示堆数,w表示每队的数量,dp[i][j]表示从第i堆开始合并j堆(包括第i堆)后的最小花费 ,dq表示最大

int sum(int i,int t){//从第i堆开始,t个石堆合,最后一次的合并的花费
    int k,s=0,k1;
    for(k=i;k<i+t;k++){
        k1=k%n;
        if(k1==0) k1=n;
        s=s+w[k1];   //这t个石堆最好两堆合并时的花费一定是这t堆石子的总和
    }
    return s;
}

int main(){
    int i,t,k;
    scanf("%d",&n);
    for(i=1;i<=n;i++){
        scanf("%d",&w[i]);
        dp[i][1]=0;//表示合并一堆的花费,没有合并则花费为0

    }
    //动态规划
    for(t=2;t<=n;t++){
        for(i=1;i<=n;i++){
            dp[i][t]=Ma_x;
            dq[i][t]=Mi_x;
            for(k=1;k<t;k++){
                dp[i][t]=min(dp[i][t],dp[i][k]+dp[(i+k-1)%n+1][t-k]+sum(i,t));
                dq[i][t]=max(dq[i][t],dq[i][k]+dq[(i+k-1)%n+1][t-k]+sum(i,t));
            }
        }
    }
    int mini=Ma_x;
    int maxi=Mi_x;
    for(i=1;i<=n;i++){//从第几堆石子开始结果最小
        mini=min(mini,dp[i][n]);
        maxi=max(maxi,dq[i][n]);
    }
    printf("%d ",mini);
    printf("%d ",maxi);
}

这段代码是一个用动态规划解决石子合并问题的示例代码。下面解释一下递推式和边界条件的含义:

  • n:表示石堆的数量。
  • w:数组,表示每个石堆的数量。
  • dp:二维数组,dp[i][j]表示从第i堆开始合并j堆(包括第i堆)后的最小花费。
  • dq:二维数组,dq[i][j]表示从第i堆开始合并j堆(包括第i堆)后的最大花费。

递推式:

dp[i][t] = min(dp[i][t], dp[i][k] + dp[(i+k-1)%n+1][t-k] + sum(i, t))
dq[i][t] = max(dq[i][t], dq[i][k] + dq[(i+k-1)%n+1][t-k] + sum(i, t))
  • dp[i][t]的计算表示从第i堆开始合并t堆石子的最小花费。它通过遍历中间位置k,将这k堆和剩余的t-k堆分别合并,并计算合并花费,然后取最小值。
  • dq[i][t]的计算表示从第i堆开始合并t堆石子的最大花费。它也通过遍历中间位置k,将这k堆和剩余的t-k堆分别合并,并计算合并花费,然后取最大值。

边界条件:

dp[i][1] = 0
  • dp[i][1]表示合并一堆石子的花费,由于只有一堆石子,所以花费为0。

最后,代码通过遍历所有堆的起始位置i,找到合并所有石堆的最小花费mini和最大花费maxi,并输出结果。

具体思想

这段代码解决的问题是给定n个石堆,并给出每个石堆的数量。任务是将这些石堆合并成一堆,合并过程中需要支付一定的花费。代码的目标是找到合并全部石堆的最小花费和最大花费。

具体的实现思路如下:

  1. 读取输入的石堆数量n。
  2. 循环读取每个石堆的数量,并初始化合并一堆的花费dp[i][1]为0。
  3. 动态规划部分:
    • 使用两个嵌套循环,外层循环变量t从2到n,内层循环变量i从1到n。
    • 对于每个t和i,初始化dp[i][t]为较大值Ma_x,dq[i][t]为较小值Mi_x。
    • 使用另一个循环变量k,从1到t-1,遍历中间位置k。
    • 更新dp[i][t]为取所有可能的合并方案中的最小值,更新dq[i][t]为取所有可能的合并方案中的最大值。
  4. 寻找最小和最大花费:
    • 初始化mini为较大值Ma_x,maxi为较小值Mi_x。
    • 使用循环从1到n,找到从每堆石子开始合并的最小花费和最大花费。
  5. 输出最小花费和最大花费。

总体思路是使用动态规划的方法,通过填充二维数组dp和dq,逐步计算合并不同堆数石子的最小和最大花费。最后,通过遍历所有堆的起始位置,找到合并所有石堆的最小花费和最大花费。

最小m段和

//最小m段和问题
#include <stdio.h>
#include <string.h>

#define N 410

int main() {
    int dp[N][N] = {0}; // 初始化为0
    int a[N] = {0};
    int n, m;
    scanf("%d %d", &n, &m); // 使用scanf读取输入

    for (int i = 1; i <= n; i++) {
        scanf("%d", &a[i]); // 读取序列的每个元素
    }

    // 状态转移方程的初始化
    for (int i = 1; i <= n; i++) {
        dp[i][1] = dp[i - 1][1] + a[i];
    }

    // 动态规划填表过程
    for (int j = 2; j <= m; j++) {
        for (int i = j; i <= n; i++) {
            int min_val = 100005;
            for (int k = 1; k < i; k++) {
                int v = (dp[k][j - 1] > (dp[i][1] - dp[k][1])) ? dp[k][j - 1] : (dp[i][1] - dp[k][1]);
                if (v < min_val) {
                    min_val = v;
                }
            }
            dp[i][j] = min_val;
        }
    }

    // 输出结果
    printf("%d\n", dp[n][m]);

    return 0;
}

递推关系:
在这段代码中,通过以下递推关系来计算dp[i][j],表示将前i个元素划分为j段的最小段和:

dp[i][j] = min(dp[k][j-1], dp[i][1] - dp[k][1]), 其中 1 <= k < i

边界条件:
 

for (int i = 1; i <= n; i++) {
    dp[i][1] = dp[i - 1][1] + a[i];
}

具体思想

这个递推关系的意思是,要计算dp[i][j],需要考虑将前k个元素划分为j-1段,并将第k+1到第i个元素作为第j段的情况。然后从这些情况中选择最小的段和。

  1. 输入n和m,表示序列长度和要划分的段数。

  2. 读取序列的元素,保存在数组a中。

  3. 初始化dp数组为0,其中dp[i][j]表示将前i个元素划分为j段的最小段和。

  4. 初始化边界条件:将前缀和存储在dp数组的第一列dp[i][1]中。即dp[i][1] = dp[i-1][1] + a[i],表示将前i个元素划分为1段时的段和。

  5. 使用动态规划填表,从2段到m段进行递推。外层循环遍历划分的段数j,内层循环遍历位置i。

    • 对于每个位置i和划分段数j,通过遍历划分点k(1 <= k < i),计算划分为j-1段的最小段和dp[k][j-1]和第j段的和dp[i][1] - dp[k][1]。选择其中较大的值作为当前位置i划分为j段的最小段和,即dp[i][j] = max(dp[k][j-1], dp[i][1] - dp[k][1])
  6. 最终结果存储在dp[n][m]中,表示将前n个元素划分为m段的最小段和。

  7. 输出最小段和:printf("%d\n", dp[n][m])

最大k乘积

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX_DIGITS 11

int maxProduct(char *num, int k) {
    int n = strlen(num);
    if (k == 1) {
        // 如果只需要一个段,那么最大乘积就是整数本身
        return atoi(num);
    }

    // 创建一个二维数组来存储中间结果
    // dp[i][j] 表示将前 j 个数字分成 i 段的最大乘积
    int dp[k][n + 1];
    memset(dp, 0, sizeof(dp));

    for (int i = 1; i <= k; ++i) {
        for (int j = i; j <= n; ++j) {
            int maxProd = 0;
            int currentProd = 1;
            for (int end = j - 1; end >= i - 1; --end) {
                currentProd *= num[end] - '0';
                maxProd = currentProd > maxProd ? currentProd : maxProd;
                currentProd /= num[end - i + 1] - '0';
                dp[i][j] = maxProd > dp[i][j] ? maxProd : dp[i][j];
            }
        }
    }

    return dp[k][n];
}

int main() {
    FILE *inputFile = fopen("input.txt", "r");
    FILE *outputFile = fopen("output.txt", "w");
    if (inputFile == NULL || outputFile == NULL) {
        perror("File opening failed");
        return EXIT_FAILURE;
    }

    int n, k;
    fscanf(inputFile, "%d %d", &n, &k);
    char num[MAX_DIGITS];
    fscanf(inputFile, "%s", num);

    int result = maxProduct(num, k);
    fprintf(outputFile, "%d\n", result);

    fclose(inputFile);
    fclose(outputFile);
    return EXIT_SUCCESS;
}

递推关系

for (int i = 1; i <= k; ++i) {
    for (int j = i; j <= n; ++j) {
        int maxProd = 0;
        int currentProd = 1;
        for (int end = j - 1; end >= i - 1; --end) {
            currentProd *= num[end] - '0';
            maxProd = currentProd > maxProd ? currentProd : maxProd;
            currentProd /= num[end - i + 1] - '0';
            dp[i][j] = maxProd > dp[i][j] ? maxProd : dp[i][j];
        }
    }
}

边界条件

  • 当 k 等于 1 时,直接返回整数本身作为最大乘积。
  • 通过创建大小为 kx(n + 1) 的二维数组来存储中间结果,并将所有元素初始化为 0,确保在递推过程中可以正确记录最大乘积。
if (k == 1) {
    // 如果只需要一个段,那么最大乘积就是整数本身
    return atoi(num);
}
//
int dp[k][n + 1];
memset(dp, 0, sizeof(dp));

具体思想

  1. 首先,我们定义了一个二维数组 dp,其中 dp[i][j] 表示将前 j 个数字分成 i 段的最大乘积。

  2. 然后,我们通过遍历不同的段数 i 和数字范围 j,来计算出 dp[i][j] 的值。

  3. 当只需要选择一个段时(即 k 等于 1),最大乘积就是整数本身,因此直接返回整数的值。

  4. 对于其他情况,我们使用三层循环来计算 dp 数组的值。

  5. 外层循环遍历不同的段数 i,从 1 到 k

  6. 内层循环遍历不同的数字范围 j,从 i 到整数的长度 n

  7. 在内层循环中,我们使用一个更小的循环来遍历当前数字范围内的所有可能的分段方式。

  8. 我们使用变量 maxProd 来记录当前分段内的最大乘积,初始值为 0。

  9. 我们使用变量 currentProd 来记录当前正在计算的分段的乘积,初始值为 1。

  10. 通过遍历当前数字范围内的所有可能的结束位置,我们逐步计算出不同分段的乘积,并更新 maxProd 为当前的最大乘积。

  11. 为了计算下一个起始位置的乘积,我们将 currentProd 除以当前分段的第一个数字。

  12. 将当前的最大乘积 maxProd 更新到 dp[i][j] 中。

  13. 最终,返回 dp[k][n] 即为将整数分成 k 段的最大乘积。

通过使用动态规划思想,我们通过从小到大的段数和数字范围,逐步计算出最大乘积,并将中间结果存储在 dp 数组中。这样可以避免重复计算,并且可以通过查表的方式获取最终的最大乘积值。

字符串比较

//字符串比较
#include <stdio.h>
#include <string.h>

const int N = 505;

int f[N][N]; // 声明一个二维数组
char a[N], b[N];
int k;

// 自定义 min 函数
int min(int a, int b) {
    return a < b ? a : b;
}

// 自定义 abs 函数,用于字符
int char_abs(char a, char b) {
    return a > b ? a - b : b - a;
}

int main(void) {
    scanf("%s %s %d", a, b, &k);
    int len_a = strlen(a);
    int len_b = strlen(b);

    // 初始化二维数组 f
    for (int i = 0; i <= len_a; i++) {
        for (int j = 0; j <= len_b; j++) {
            f[i][j] = k * (i + j);
        }
    }

    // 动态规划填表
    for (int i = 1; i <= len_a; i++) {
        for (int j = 1; j <= len_b; j++) {
            f[i][j] = min(min(f[i - 1][j], f[i][j - 1]) + k, f[i - 1][j - 1] + char_abs(a[i - 1], b[j - 1]));
        }
    }

    printf("%d\n", f[len_a][len_b]);

    return 0;
}

递推关系

// 动态规划填表
for (int i = 1; i <= len_a; i++) {
    for (int j = 1; j <= len_b; j++) {
        f[i][j] = min(min(f[i - 1][j], f[i][j - 1]) + k, f[i - 1][j - 1] + char_abs(a[i - 1], b[j - 1]));
    }
}

边界条件

// 初始化二维数组 f
for (int i = 0; i <= len_a; i++) {
    for (int j = 0; j <= len_b; j++) {
        f[i][j] = k * (i + j);
    }
}

数字三角形

#include <stdio.h>

#define MAXN 100

int n;
int D[MAXN][MAXN];
int maxSum[MAXN][MAXN];
int id = 1;

int fun(int i, int j) {
    int x, y;
    if (maxSum[i][j] != -1) { // 如果之前有计算过,直接取记录好的数,避免进行多次计算
        return maxSum[i][j];
    }

    if (i == n) { // 如果i到数组的底部最后一行,那么最大和就是它本身那个数
        maxSum[i][j] = D[i][j];
    } else {
        x = fun(i + 1, j);
        y = fun(i + 1, j + 1);
        maxSum[i][j] = (x > y) ? x + D[i][j] : y + D[i][j];
    }
    return maxSum[i][j];
}

int main() {
    int i, j;
    while (scanf("%d", &n) == 1) { // 读取n,直到输入失败
        for (i = 0; i < n; i++) {
            for (j = 0; j < i + 1; j++) {
                scanf("%d", &D[i][j]);
                maxSum[i][j] = -1; // 标记每个数的最大值为-1
            }
        }
        printf("Triangle #%d: %d\n", id++, fun(0, 0));
    }
    return 0;
}

递推

int fun(int i, int j) {
    int x, y;
    if (maxSum[i][j] != -1) {
        return maxSum[i][j];
    }

    if (i == n) {
        maxSum[i][j] = D[i][j];
    } else {
        x = fun(i + 1, j);
        y = fun(i + 1, j + 1);
        maxSum[i][j] = (x > y) ? x + D[i][j] : y + D[i][j];
    }
    return maxSum[i][j];
}

边界条件

if (i == n) {
    maxSum[i][j] = D[i][j];
}

乘法表

#include <stdio.h>
#include <string.h>

#define MAXN 50

int result[MAXN][MAXN][3];

// 自定义最小值函数
int min(int a, int b) {
    return a < b ? a : b;
}

int main() {
    char str[MAXN];
    int n;

    scanf("%s", str); // 读取字符串
    n = strlen(str);

    // 初始化整个三维数组为0
    memset(result, 0, sizeof(result));

    // 相当于是求边界的值
    for (int i = 0; i < n; ++i) {
        if (str[i] == 'a') result[i + 1][i + 1][0] = 1;
        if (str[i] == 'b') result[i + 1][i + 1][1] = 1;
        if (str[i] == 'c') result[i + 1][i + 1][2] = 1;
    }

    // 区间长度
    for (int r = 2; r <= n; ++r) {
        // 区间起始
        for (int i = 0; i <= n - r; ++i) {
            int j = i + r - 1;
            for (int k = i; k < j; k++) { // k表示分割的位置
                result[i + 1][j + 1][0] += result[i + 1][k + 1][0] * result[k + 2][j + 1][2] + result[i + 1][k + 1][1] * result[k + 2][j + 1][2] + result[i + 1][k + 1][2] * result[k + 2][j + 1][0];
                result[i + 1][j + 1][1] += result[i + 1][k + 1][0] * result[k + 2][j + 1][0] + result[i + 1][k + 1][0] * result[k + 2][j + 1][1] + result[i + 1][k + 1][1] * result[k + 2][j + 1][1];
                result[i + 1][j + 1][2] += result[i + 1][k + 1][1] * result[k + 2][j + 1][0] + result[i + 1][k + 1][2] * result[k + 2][j + 1][1] + result[i + 1][k + 1][2] * result[k + 2][j + 1][2];
            }
        }
    }

    printf("%d\n", result[1][n][0]);
    return 0;
}

 递推

for (int r = 2; r <= n; ++r) {
    for (int i = 0; i <= n - r; ++i) {
        int j = i + r - 1;
        for (int k = i; k < j; k++) {
            result[i + 1][j + 1][0] += result[i + 1][k + 1][0] * result[k + 2][j + 1][2] + result[i + 1][k + 1][1] * result[k + 2][j + 1][2] + result[i + 1][k + 1][2] * result[k + 2][j + 1][0];
            result[i + 1][j + 1][1] += result[i + 1][k + 1][0] * result[k + 2][j + 1][0] + result[i + 1][k + 1][0] * result[k + 2][j + 1][1] + result[i + 1][k + 1][1] * result[k + 2][j + 1][1];
            result[i + 1][j + 1][2] += result[i + 1][k + 1][1] * result[k + 2][j + 1][0] + result[i + 1][k + 1][2] * result[k + 2][j + 1][1] + result[i + 1][k + 1][2] * result[k + 2][j + 1][2];
        }
    }
}

边界条件

for (int i = 0; i < n; ++i) {
    if (str[i] == 'a') result[i + 1][i + 1][0] = 1;
    if (str[i] == 'b') result[i + 1][i + 1][1] = 1;
    if (str[i] == 'c') result[i + 1][i + 1][2] = 1;
}

租用游艇

#include <stdio.h>
#include <math.h>

#define MAXN 100

int r[MAXN][MAXN]; // r[i][j] 是第 i 站到第 j 站的费用
int n; // n 站
int cost[MAXN][MAXN]; // cost[i][j] 是第 i 站到第 j 站的最小费用
int costt[MAXN]; // costt[i] 是第 1 站到第 i 站的最小费用

// 自定义 min 函数
int min(int a, int b) {
    return a < b ? a : b;
}

int dp1() {
    int i, j, k, t;
    for (i = 1; i < n; i++) {
        for (j = i + 2; j <= n; j++) {
            t = 99999;
            for (k = i + 1; k < j; k++) {
                t = min(t, cost[i][k] + cost[k][j]);
            }
            cost[i][j] = min(t, r[i][j]);
        }
    }
    printf("方法:%d\n", cost[1][n]);
}

int main() {
    scanf("%d", &n);
    for (int i = 1; i < n; i++) {
        for (int j = i + 1; j <= n; j++) {
            scanf("%d", &r[i][j]);
            cost[i][j] = r[i][j];
        }
    }
    dp1();

    return 0;
}

递推

for (i = 1; i < n; i++) {
    for (j = i + 2; j <= n; j++) {
        t = 99999;
        for (k = i + 1; k < j; k++) {
            t = min(t, cost[i][k] + cost[k][j]);
        }
        cost[i][j] = min(t, r[i][j]);
    }
}

边界条件

for (int i = 1; i < n; i++) {
    for (int j = i + 1; j <= n; j++) {
        scanf("%d", &r[i][j]);
        cost[i][j] = r[i][j];
    }
}

m处理器

#include <stdio.h>
#include <math.h>

#define N 10010

int n, m;
double ans;
int a[N], sum[N];
double dp[N][N]; // dp[i][j] 表示前i个数据包被划分成j段的max{f(rm,rm+1)} 0<=m<=j-1
double f[N][N];

// 自定义 min 函数
double min(double a, double b) {
    return a < b ? a : b;
}

// 自定义 max 函数
double max(double a, double b) {
    return a > b ? a : b;
}

int main() {
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++) {
        scanf("%d", &a[i]);
        sum[i] = sum[i - 1] + pow(a[i], 2);
    }
    for (int i = 1; i <= n; i++) {
        for (int j = i; j <= n; j++) {
            f[i][j] = sqrt(sum[j] - sum[i - 1]);
        }
    }

    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            dp[i][j] = 9999;
        }
    }
    for (int i = 1; i <= n; i++) {
        dp[i][1] = f[1][i];
        for (int j = 2; j <= min(m, i); j++) {
            for (int k = j - 1; k < i; k++) {
                dp[i][j] = min(dp[i][j], max(dp[k][j - 1], f[k + 1][i]));
            }
        }
    }
    printf("%.3f", dp[n][m]);
    return 0;
}

 递推关系

for (int i = 1; i <= n; i++) {
    dp[i][1] = f[1][i];
    for (int j = 2; j <= min(m, i); j++) {
        for (int k = j - 1; k < i; k++) {
            dp[i][j] = min(dp[i][j], max(dp[k][j - 1], f[k + 1][i]));
        }
    }
}

 边界条件

for (int i = 1; i <= n; i++) {
    dp[i][1] = f[1][i];
    for (int j = 1; j <= n; j++) {
        dp[i][j] = 9999;
    }
}

 活动安排

背包问题

最优装载问题

哈夫曼编码

多机调度问题

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值