zoukankan      html  css  js  c++  java
  • 小烈送菜——奇怪的dp

    小烈送菜

    题目描述

    小烈一下碰碰车就被乐满地的工作人员抓住了。作为扰乱秩序的惩罚,小烈必须去乐满地里的“漓江村”饭店端盘子。

    服务员的工作很繁忙。他们要上菜,同时要使顾客们尽量高兴。一位服务生为 (n) 个顾客上菜。这 (n) 个顾客坐成一排,小烈从一端的厨房中端出(n)盘菜(不要问我为什么小烈能一下子端住 (2500) 盘菜,他就是能)为 (n) 个顾客各上一道相同的菜。

    显然,小烈需要走一个来回,如图:

    本来,小烈可以按 (1,2,3...n) 的顺序一次给每个顾客上菜,但是,聪明的小烈通过观察发现,每个顾客都有一个开心值 (H_1)(H_2)(H_3)...(H_n) ,离厨房最近的为 (H_1) ,然后依次为 (H_2)(H_3)...(H_n) 。若小烈给第 (j) 位顾客上菜前刚刚为第 (i) 位顾客上菜,则第 (j) 位就会高兴,产生高兴指数 (W_j=H_i imes H_j) 。这样,如果小烈按一定的方式调整上菜顺序,可以得到更高的高兴指数。现在小烈想知道用某一方法可达到的 (n) 位顾客高兴指数之和的最大值 (S) 。因为顾客越高兴,给小烈的小费越多。第一位上菜的顾客不产生高兴值。

    输入格式

    第一行一个整数 (n) ,顾客的数目。

    第二行 (n) 个数,第 (i) 个数表示第 (i) 位顾客的开心值。各个数字用空格隔开。

    输出格式

    一个数 (s) ,为高兴指数的最大值。

    样例

    样例输入

    3
    7 1 9
    

    样例输出

    72
    

    数据范围与提示

    样例解释:从左往右上 (1) 的菜,再上 (9) 的菜,高兴值是 (0 imes 1+1 imes 9) ,从右往左走回来的时候上 (7) 的菜,高兴值是 (7 imes 9) ,总的高兴值就是 (72)
    对于 (30\%) 的数据(nleq9,nin N^+)
    对于 (70\%) 的数据(nleq1500,nin N^+)
    对于 (100\%) 的数据(nleq2500,nin N^+)
    所有数字小于(含结果) (2147483648)

    思路

    题意中的来回两次直接 (dp) 的话,还需要记录第一次走的路径,很麻烦。

    根据之前的方格取数,我们也可以把这个题转换成两个小烈送菜。

    定义 (dp[i][j]) 为有两个小烈同时从厨房开始送菜,第一个小烈到达 (i) 的位置,第二个小烈到达 (j) 的位置,所能取到的最大值。

    有一个很重要的性质:(dp[i][j]=dp[j][i])
    第一个小烈送到 (i) 的位置,第二个小烈送到 (j) 的位置与第一个小烈送到 (j) 的位置,第二个小烈送到 (i) 的位置所得的结果是相同的。

    动态转移方程:

    第一个小烈由 (i) 送到 (i+1) 的位置。

    (dp[i+1][j]=max(dp[i+1][j],dp[i][j]+a[i]*a[i+1]))

    或者第二个小烈由 (j) 送到 (i+1) 的位置。

    (dp[i][i+1]=max(dp[i][i+1],dp[i][j]+a[j]*a[i+1]))

    根据之前的性质,可以把转移方程转化为:

    (dp[i+1][i]=max(dp[i+1][i],dp[i][j]+a[j]*a[i+1]))

    这样保证了 (dp[i][j]) 中的 (i) 一定会大于 (j),便于处理。

    最后在寻找一遍最大值即可。

    代码

    #include <bits/stdc++.h>
    using namespace std;
    
    const int maxn=2500+50;
    int n,ans;
    int a[maxn];
    int dp[maxn][maxn];
    
    int main(){
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
        }
        for(int i=1;i<=n;i++){
            for(int j=0;j<i;j++){
                dp[i+1][j]=max(dp[i+1][j],dp[i][j]+a[i]*a[i+1]);//前面那个处于i位置的小烈送i+1的菜
                dp[i+1][i]=max(dp[i+1][i],dp[i][j]+a[j]*a[i+1]);//后面那个处于j位置的小烈送i+1的菜,并根据性质翻转
            }
        }
        for(int i=0;i<n;i++){
            ans=max(ans,dp[n][i]+a[n]*a[i]);//前面那个小烈送到n的位置,但是不确定后面那个小烈在哪里,所以以此枚举
        }
        cout<<ans<<endl;
        return 0;
    }
    
  • 相关阅读:
    C#读写xml文件
    XSD(XML Schema Definition)用法实例介绍以及C#使用xsd文件验证XML格式
    C#异步批量下载文件
    echarts的markline的使用 y轴预警线
    Bootstrap-table 增删改查
    二维数组 和 稀疏数组的相互转换 及 数据存入文件中
    Bootstrap-table实现动态合并相同行
    echarts 中 参数的详讲
    BootstrapTable的简单使用教程
    遍历List 中 Map 的值
  • 原文地址:https://www.cnblogs.com/Rubyonly233/p/13227210.html
Copyright © 2011-2022 走看看