题目描述: 给定一个大小为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 ****************************************************************/