题目1537:买卖股票

题目描述:
给定一个大小为n的数组,数组的元素a[i]代表第i天的股票价格。
设计一个算法,计算在最多允许买卖k次(一买一卖记为一次)的条件下的最大收益。
需要注意的是,你不能同时拥有两份股票。也就是说在下次买入前,你必须把手头上原有的股票先卖掉。
输入:
输入可能包含多个测试案例。
对于每个测试案例,输入的第一行为两个整数n和k(1<=n,k<=1000)。
输入的第二行包括n个整数,范围在[0,10000),代表数组中的元素。
输出:
对应每个测试案例,输出最大获益。
样例输入:
5 1
3 4 5 1 4
7 2
1 2 3 5 6 1 7
样例输出:
3
11
来源:
Leetcode 加强版本

本来这一题完全没有思路的。主要是想到了用dp[i]表示前i天的最大收益。但是不知道怎么处理k次。。还是做题太少。这种有两个条件限制的题明显用dp[i][j]来做。比如0-1背包问题,最原始的想法是考虑到背包体积v和i件物品dp[i][j]表示体积不超过j时,前i个物品能达到的最大价值。LCS问题,考虑到两个字符串dp[i][j]表示S1的前x个字符和S2的前j个字符最长公共子序列。

所以这题dp[i][j]表示前j天完成i次交易获得的最大收入。

1) dp[0][j] = 0; dp[i][0] = 0;dp[i][1] = 0;

2)dp[i][j] = max{dp[i][j-1], max{dp[i - 1][k] + a[j] - a[k]}(1<= k <j)} (1<= i<=交易次数, 2<=j<=n)

总状态数为O(n * k).状态转移方程可以对每个i,维护一个max = dp[i -1][k] - a[k],这样转移次数为O(1),时间复杂度为O(n * k),否则遍历为O(n),时间复杂度为O(n^2 * k)会超时。

AC代码:

#include <stdio.h>
 
const int MAX = 1001;
 
int dp[MAX][MAX];
 
int main()
{
    int list[1010];
    int n, k;
    while(scanf("%d %d", &n, &k) == 2)
    {
        for(int i = 1; i <= n; i++)
            scanf("%d", &list[i]);
        for(int i = 0; i <= n; i++)
        {
            dp[0][i] = 0;
        }
        for(int i = 0; i <= k ;i ++)
        {
            dp[i][0] = 0;
            dp[i][1] = 0;
        }
        for(int i = 1; i <= k; i++)
        {
            int max = dp[i - 1][1] - list[1];
            for(int j = 2; j <= n; j++)
            {
                dp[i][j] = dp[i][j - 1] > (max + list[j]) ? dp[i][j - 1] : (max + list[j]);
                int tmp = dp[i - 1][j] - list[j];
                max = tmp > max ? tmp : max;
            }
        }
        printf("%d
", dp[k][n]);
    }
    return 0;
}
/**************************************************************
    Problem: 1537
    User: Qinger
    Language: C++
    Result: Accepted
    Time:40 ms
    Memory:4936 kb
****************************************************************/
原文地址:https://www.cnblogs.com/xyqhello/p/3591672.html