我如何以更快,更准确的方式解决这个问题?

时间:2013-01-04 06:03:21

标签: c algorithm dynamic-programming greedy

以下是问题:

  拉姆是一个懒惰的农民。他从父亲那里继承了一个相当大的农场和一座漂亮的房子。拉姆将农场土地出租给其他人并获得了相当可观的收入。他的父亲过去常常在家养一头水牛并卖掉牛奶,但水牛在他父亲吃完后几天就死了   Ramu也想从水牛那里赚钱,但是以一种完全不同的方式。他认为他的未来在于推测水牛。在他村里的市场上,每天都会买卖水牛。价格在一年中波动,但在任何一天价格总是相同的   他决定在价格低的时候买水牛,在价格高的时候卖掉水牛,并在此过程中积累巨额财富。不幸的是,他的房子只有一头水牛的空间,所以他随时都可以拥有一头水牛   在他进入水牛市场之前,他决定检查过去几天检查水牛价格的变化,并确定他可以获得的最大利润。假设,过去10天水牛的价格变化为

     

10 12 8 11 11 10 12 15 13 10

     拉姆是一个懒惰的家伙,他估计在过去的10天里,他最多愿意去市场5次(每次购买或出售一头水牛)。鉴于此,他可以获得的最大利润是9卢比。为了做到这一点,他在第1天购买了一头水牛,在第2天卖掉了它,在第3天买了一头水牛,并在第8天买了它。如果他不那么懒,并且愿意6次去市场,那么他可以赚更多的钱。他本可以在第1天购买,第2天购买,第3天购买,第4天购买,第6天购买,第8天购买,获利10卢比。   你的任务是帮助Ramu计算他可以通过推测水牛获得的最大金额,给出一段时间内每日水牛价格的历史,以及Ramu在此期间愿意进入市场的次数的限制。

     

输入格式
  输入的第一行包含两个整数N和K,其中N是价格数据可用的天数,K是Ramu愿意访问牛市的最大次数。接下来的N行(第2,3行,......,N + 1)各包含一个正整数。第i + 1行的整数,1≤i≤N,表示第i天的水牛价格。

     

输出格式
  一个非负整数,表示如果Ramu最多可以进入K市场,那么Ramu可以获得的最大利润。
  您可以假设N≤400且K≤400   时间限制= 3秒。

如果我们没有约束k,我们可以使用贪心方法来解决这个问题,

 while c < N
   i = c
   while price[day i] > price[day i+1] increment i;
   j = i+1
   while price[day j] < price[day j+1] increment j;
   add price[day j] - price[day i] to out profit
   c = j+1

但是我们不能在这里使用它,因为是否在特定日期访问取决于我们访问了多少天。 这是我试过的

 //Store all the pairs generated in the previous algorithm in a linked list L, each
 //element has two attributes buy,sell
 while length of L > k/2
       find an element i in the list such the (L[i].sell- L[i-1].buy) - 
       (L[i-1].buy - L[i-1].sell) - (L[i].buy - L[i].sell) is maximized.
       Then set L[i-1].sell to L[i].sell and delete i from the list

这是一个在线评判的问题,当我提交它时,我的某些测试用例超出了时间限制,对某些测试用例的答案错误,只有一个测试用例正确。

我的方法有什么问题,它怎么会慢,因为它需要大约O(NK)时间,其中N和K < 400.
如何改进算法以正确解决问题?

编辑:这不是作业,我在这里发现了这个问题:http://opc.iarcs.org.in/index.php/problems/BUFFALOES

1 个答案:

答案 0 :(得分:2)

我没有仔细分析过,但在我看来,你对懒惰农民的想法有点过于贪婪。我很难看到链接列表或其上的操作。

我认为在不担心效率的情况下考虑这个问题的好方法是将其转换为图表,其中每天都是图表中的节点。

如果我们有一个勤奋的交易者愿意尽可能多地访问市场,它看起来像下面的图1,我在你的例子的前几天。弧线从图表中的每天稍后日开始绘制,弧线按如下方式加权:

  • 连续几天必须有一个弧 - 要么用买入的利润加权,然后在第二天卖出,要么(如果这样的活动是损失的话)加权零
  • 如果利润为正,非连续日只需要一个弧。 (虽然您可以为非盈利对绘制零重量弧线,但我已将它们抑制在下方,以便图形可读。)

此图表需要进行O(N ^ 2)比较/减法。获得此图表后,为农民找到最佳计划等同于通过图表找到最长路径(例如,从第一天到具有最大弧值总和的最后一天的路径)。通常,通过图形找到最长路径是NP-Complete,但在这种情况下,我们有一个有向无环图 - 您可以简单地否定所有边权重,并在多项式时间内使用Dijkstra算法找到最短路径。

Figure 1:  Non-Lazy Farmer

为了对付懒惰的农民,你需要调整这个图形结构,使其“计算”非零弧。我们通过使图表更大来实现这一目标。更大。如果农民愿意去市场进行k次旅行,那么他就有买入/卖出(k / 2)买入/卖出。让我们称这个数字为X,并且每X + 1次绘制图形的节点。

同一行中的每个连续弧(无论当天的利润如何)的权重为0.正长度的弧重定向到下面的行。图2显示了如果农民愿意进行4次市场旅行,以及2次买入/卖出机会的情况。您还可以免费添加一个虚拟的“终端节点”,您可以从每行的末尾开始。

您可以看到,通过确保每个获利机会从一行到另一行的移动来“计算”利润弧,并且永远不会有机会多次使用同一行。再次,你可以找到找到正确答案的最长途径;并且图表再次是非循环的,因此可以将其转换为最短路径问题并在多项式时间内求解。

坏消息是,节点和弧数大幅增加。如果k = N,则可能有O(N ^ 2)而不是N个节点。同样地,代替O(N ^ 2)个弧,你有O(N ^ 3)。

Figure 2: Lazy Farmer

通过将问题转换为网格,您可以在时间和空间上做得更好,类似于最常见的子序列问题,但这至少说明了问题的结构。