POJ 1651 Multiplication Puzzle

题意:

给你一堆卡片,每张卡片上有一个数,按顺序依次排放。每次从其中抽取一张卡片(不能在首尾取卡片),每次取卡片都会得到一个分数,分数由所取卡片与其前后两张卡片分数的乘积决定。当卡片只剩两张时结束,最终分数为每次得分的总和,求总和的最小值。

如:10 1 50 20 5 

a. 若取卡片的顺序为1 20 50

第一次得分:10*1*50 = 500

第二次得分:50*20*5 = 5000

第三次得分:10*50*5 = 2500

总分: 500 + 5000 + 2500 = 8000


b. 若取卡片的顺序为 50 20 1

第一次得分:1*50*20 = 1000

第二次得分:1*20*5   = 100

第三次得分:10*1*5   = 50

总分: 1000 + 100 +50 = 1150


解题分析:

首尾两张卡片始终不会被取走,最后一张卡片的得分最小值由中间卡片的最小值确定。由最后一张卡片可以将整个序列分成两部分。最终答案为 左边卡片被取完得到的最小值 + 首卡片*最后所取卡片*尾卡片 + 右边卡片被取完得到的最小值。左右两部分的求取可以依次DP下去,不难得到DP方程:

dp[i][j] = min {dp[i][k]+arry[i]*arry[k]*arry[j]+dp[k][j],  i< k < j}

dp[i][j] 表示取完卡片i ~ j 之间的所有卡片所得总分的最小值

需要注意的地方:  初始化


题解代码:

// by weikd
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>

using namespace std;

const int maxn = 200;
int dp[maxn][maxn];
int arry[maxn];
int ans;

int main()
{
    int n;
    while (scanf("%d", &n) != EOF) {
        memset(dp, 127, sizeof(dp));                     // 初始化为最大值
        for (int i = 1; i <= n; ++i) {
            scanf("%d", &arry[i]);                       // 为了思路清晰,从ayyy[1] 开始读数据
        }
        for (int i = 1; i <= n-2; ++i) {
            dp[i][i+2] = arry[i]*arry[i+1]*arry[i+2];    // 初始化连续的三张卡片的得分
            dp[i][i+1] = 0;                              // 连续的两张卡片不符合题意,初始化为0
        }
        dp[n-1][n] = 0;
        for (int i = n-2; i >= 1; --i) {                 // 从后往前推dp[i][j]
            for (int j = i+2; j <= n; ++j) {
                for (int k = i+1; k < j; k++) {
                    dp[i][j] = min(dp[i][j], dp[i][k]+dp[k][j]+arry[i]*arry[k]*arry[j]);
                }
            }
        }
        printf("%d\n", dp[1][n]);                        // 答案为dp[1][n]
    }

    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值