贪心+最优策略 7.21牛客暑期多校训练营二 D

4.

White Cloud has built n stores numbered from 1 to n.

White Rabbit wants to visit these stores in the order from 1 to n.The store numbered i has a price a[i] representing that White Rabbit can spend a[i] dollars to buy a productor sell a product to get a[i] dollars when it is in the i-th store.The product is too heavy so that White Rabbit can only take one product at the same time.White Rabbit wants to know the maximum profit after visiting all stores.Also, White Rabbit wants to know the minimum number of transactions while geting the maximum profit.Notice that White Rabbit has infinite money initially.

输入描述:The first line contains an integer T(0<T<=5), denoting the number of test cases.In each test case, there is one integer n(0<n<=100000) in the first line,denoting the number of stores.For the next line, There are n integers in range [0,2147483648), denoting a[1..n].

输出描述:For each test case, print a single line containing 2 integers, denoting the maximum profit and the minimumnumber of transactions.

示例1:输入110 7 6 8输出3 4

题目大意:

​ n个商店一路走下去,一次只能带一件商品tv,商店有价值v[i],可以选择花v[i]买或卖v[i] (赚v[i]-tv的钱), 问最多能赚多少钱,且交易次数最少。

想法:

​ 最先想到贪心。可是因为前面的买卖并不知道对后面来说是不是最优解,所以不行。要用dp。顺着上题想dp[i][0] \ [1]买i还是不买,显然不行的。想不到最优策略,就写不来dp。想试试dfs,没有策略的话,2^n个结果肯定不行。总而言之,就是想不到最优策略。(没细想,都去写A了)。后来靠刘老板二十几分钟A了出来。666

思路:

​ 1、贪心。后面的商品比前面的贵的话,一定要买前面的,卖后面的。

​ 2、在价格曲线上的连续递增的一段上,在开头买,在末尾卖肯定是最优的(利润最多,同时交易次数最少),中间多几次操作,利润是相同的

​ 3、最多的利润,即每条递增段的头尾相减之和

​ 4、利润最多的交易次数即连续递增段数量

标准题解

首先,如果a[i]=a[i+1],则可以删掉第i+1个商店。因为任何在第i+1个商店进行的交易都可以转为在第i个商店进行,且收益不变。之后,如果a[i]<a[i+1],则离开第i个商店时一定要带上一件商品。如果a[i]>a[i+1],则离开第i个商店时一定要空着手。这样,第一问的答案max(a[i+1]-a[i],0)的累加,第二问的答案就为长度>1的极大递增连续段的数量。

标准题解中还有DP思路

f[i][0/1]表示已经访问完了i个商店,你手中是否有商品,此时的最大收益。g[i][0/1]表示当f[i][j]取最大值时最少的交易次数。

AC代码:

#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long LL;
int main(){
    int t;
    cin>>t;
    while(t--) {
        int n;
        LL a[100005];
         
        memset(a, 0, sizeof(a));
         
        cin>>n;
        for (int i = 0; i < n; i++) {
            cin>>a[i];
        }
         
        LL sum = 0, cnt = 0;
        bool flag = false;
        for (int i = 0; i < n-1; i++) {
            if(a[i] < a[i+1]) {  //贪心
                sum += a[i+1] - a[i];
//              printf("a[i] = %d a[i+1] = %d\n", a[i], a[i+1]);
                if(!flag) {     //是不是连续递增段的头尾    
                    cnt++;
                }
                flag = true;
            } else if(a[i] == a[i+1]){
//              do nothing 不能与上面合并
            } else{
                flag = false;
            }
        }
        cout<<sum<<" "<<cnt*2<<endl;
    }
   
    return 0;
}

猜你喜欢

转载自blog.csdn.net/keeeepgo/article/details/81163186