找到树中的最短路径

时间:2012-11-05 00:34:48

标签: algorithm tree graph-theory dynamic-programming shortest-path

我遇到了解决以下问题的麻烦。

假设一家公司需要在未来五年内拥有一台机器。每台新机器的价格为100,000美元。在运行的第i年运行机器的年度成本如下:C1 = 6000美元,C2 = 8000美元,C3 = 12,000美元。在交易之前,机器可以保持长达三年。这意味着机器可以在前两年保存或交易,并且必须在其年龄为三年时进行交易。 i年后的价值交易是t1 = 80,000美元,t2 = 60,000美元,t3 = 50,000美元。如果公司要在0年内购买新机器,公司如何在五年期间(第0年到第5年)最大限度地降低成本?
基于动态编程设计最佳解决方案。

可以使用树来表示此问题。这是图表。 tree representation

现在我认为在上面的树中寻找最短路径会给我最优解。但我不知道该怎么做。这是我的问题,

欢迎任何其他建议。

伙计们,我想为这个问题提供一些指导和帮助。 (不要认为这是要求我完成作业的请求。)我已经找到了这个问题here的完整Java实现。但它不使用动态编程来解决问题。提前谢谢。

6 个答案:

答案 0 :(得分:2)

该公司希望将成本降至5年以下。在0年级,他们将购买一台机器和一台机器。每年他们必须决定机器是否被保管或交易。为了达到最佳解决方案,我们必须在每年年底做出一系列选择。当我们做出每一个选择时,经常出现同样的子问题。     因此,我们到达一个位置,在该位置,给定的子问题可能来自多个部分选择集。在设计基于动态规划的最优解决方案时,我们可以通过将解决方案与子问题相结合来解决问题。

让阶段对应于每一年。州是该年机器的时代。决定是保留机器还是换新机器。假设机器在时间t为x岁,则将Ft(x)设为从时间t到时间5产生的最小成本。 基本情况

由于我们必须在5年底交易机器F5(x)= - S [x]

  • 在0年级我们买了一台新机器F0(1)= N + M [1] + F1(0)
  • 在5岁至3岁的范围内:0
  • 保持现有机器最多3年:Ft(3)= N + M [0] + Ft + 1(0);吨≠0,1,2

      

    def Fxy(自我,时间,年龄):

        if self.Matrix[time][age]==None: <- Overlaping subproblems avoided
            if(time>5 or age>2):
                return 0
            if time==5:
                self.Matrix[time][age]=T=self.S[age]
                self.Flag[time][age]='TRADE'                
            elif time==0: 
           self.Matrix[time][age]=K=self.N+self.M[0]+self.Fxy(time+1,time)
                self.Flag[time][age]='KEEP'             
            elif time==3 and age==2: 
           self.Matrix[time][age]=T=self.S[age]+self.N+self.M[0]+self.Fxy(time+1,0)
    
                self.Flag[time][age]='TRADE'                
            else:
                T=self.S[age]+self.N+self.M[0]+self.Fxy(time+1,0)                   
                if age+1<len(self.Matrix[0]):
                    K=self.M[age+1]+self.Fxy(time+1,age+1)  
                else:
                    K=self.M[age+1]
                self.Matrix[time][age]=min(T,K)             
                if(self.Matrix[time][age]==T and self.Matrix[time][age]==K):
                    self.Flag[time][age]='TRADE OR KEEP'
                elif(self.Matrix[time][age]==T):
                    self.Flag[time][age]='TRADE'
                else:
                    self.Flag[time][age]='KEEP'
            return self.Matrix[time][age]
        else:           
            return self.Matrix[time][age]
    

通过绘制包含所有可能路径的决策树,可以实现最佳解决方案。采用最低成本路径。我们使用递归算法遍历每个树级别&amp;制作当前决策点发生的路径。 例如:当它遍历F1(0)时,它有“交易或保持”决定与它绑定。然后我们可以遍历两条可能的路径。当它遍历F2(1)时,因为它有'KEEP'决定然后递归地我们遍历F3(2),即正确的孩子。当'TRADE'遇到时,左边的孩子一直到达叶子。

def recursePath(self,x,y):
    if(x==5):           
        self.dic[x].append(self.Flag[x][y])         
        return self.Flag[x][y]

    else:   
        if(self.Flag[x][y]=='TRADE OR KEEP'):
            self.recursePath(x+1,y)             
            self.recursePath(x+1,y+1)               

        if(self.Flag[x][y]=='KEEP'):
            self.recursePath(x+1,y+1)               

        if(self.Flag[x][y]=='TRADE'):
            self.recursePath(x+1,y)         

        self.dic[x].append(self.Flag[x][y])         
        return self.Flag[x][y]

答案 1 :(得分:1)

如果你想要像Dijkstra这样的东西,你为什么不做Dijkstra?你需要在图解释中改变一些东西,但它似乎非常可行:

Dijkstra将根据最低成本标准解决节点问题。将该标准定为“公司亏钱”。您还将解决Dijkstra中的节点,您需要确定节点究竟是什么。将节点视为时间和属性状态,例如第4年使用x岁的工作机器。在第0年,丢失的钱将是0,你将没有机器。然后添加所有可能的边/选择/状态转换,这里是“买一台机器”。您最终会在Dijkstra PQ(第1年,1岁的工作机器)上获得一个新节点,并且需要一定的费用。

从此,您可以随时出售机器(产生[年1,无机器]节点),购买新机器[第1年,新机器]或继续相同([2年级,机器年龄] 2])。你只需继续开发最短的路径树,直到第5年(或更多)你拥有你想要的一切。

然后你有一组节点[年i,机器年龄j]。为了在第一年为您的公司找到最佳选择,只需查看它的所有可能性(我认为它总是[年我,没有机器])来获得答案。

由于Dijkstra是一种全对最短路径算法,它为您提供了所有年份的最佳路径

编辑:java的一些伪代码 首先,您应该创建一个节点对象/类来保存您的节点信息。

Node{
int cost;
int year;
int ageOfMachine;
}

然后你可以添加节点并解决它们。确保您的PQ根据成本字段对节点进行排序。从根开始:

PQ<Node> PQ=new PriorityQueue<Node>();
Node root= new Root(0,0,-1);//0 cost, year 0 and no machine) 
PQ.offer(root); 
int [] best= new int[years+1];
//set best[0..years] equal to a very large negative number
while(!PQ.isEmpty()){
   Node n=PQ.poll();
   int y=n.year;
   int a=n.ageOfMachine;
   int c=n.cost;
   if(already have a cost for year y and machine of age a)continue;
   else{
      add [year y, age a, cost c] to list of settled nodes;
      //examine all possible further actions
      //add nodes for keeping a machine and trading a machine
      PQ.offer(new Node(cost+profit selling current-cost of new machine,year+1,1));
      PQ.offer(new Node(cost,year+1,age+1);//only if your machine can last an extra year
      //check to see if you've found the best way of business in year i
      if(cost+profit selling current>best[i])best[i]=cost+profit selling current;
   }
}

这些方面的某些内容将为您提供最佳实践,以达到最佳成本[i]

答案 2 :(得分:1)

我想我找到了一个更简单的动态程序解决方案。

建议成本(n)是在第n年出售时的全部成本。保持机器1年,2年,3年的成本是cost1,cost2,cost3(在这个问题上是26000,54000,76000)。

然后我们可以将问题分成这样的子问题:

**Cost(n)= MIN( Cost(n-1)+cost1, Cost(n-2)+cost2, Cost(n-3)+cost3 );**

所以我们可以用'自下而上的方式'来计算它,这只是O(n)。

我已经使用C:

实现并测试了它
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct costAndSell_S{
    int lastSellYear;
    int cost;
};

int operatCost[3]={6000,8000,12000};
int sellGain[3]={80000,60000,50000};
int newMachinValue=100000;
int sellCost[3];
struct costAndSell_S costAndSell[20];

void initSellCost(){

    memset( costAndSell, 0, sizeof(costAndSell));
    sellCost[0]=operatCost[0]+newMachinValue-sellGain[0];
    sellCost[1]=operatCost[0]+operatCost[1]+newMachinValue-sellGain[1];
    sellCost[2]=operatCost[0]+operatCost[1]+operatCost[2]+newMachinValue-sellGain[2];

    costAndSell[0].cost=100000;
    return;
}

int sellAt( int year ){
    if ( year<0){
        return(costAndSell[0].cost );
    }
    return costAndSell[year].cost;
}

int minCost( int i1, int i2, int i3 ){
    if ( (i1<=i2) && (i1<=i3) ){
        return(0);
    }else if ( (i2<=i1) && (i2<=i3) ){
        return(1);
    }else if ( (i3<=i1) && (i3<=i2) ){
        return(2);
    }
}
void findBestPath( int lastYear ){
    int i;
    int rtn;
    int sellYear;
    for( i=1; i<=lastYear; i++ ){
        rtn=minCost( sellAt(i-1)+sellCost[0], sellAt(i-2)+sellCost[1], sellAt(i-3)+sellCost[2]);
        switch (rtn){
            case 0:
                costAndSell[i].cost=costAndSell[i-1].cost+sellCost[0];
                costAndSell[i].lastSellYear=i-1;
                break;
            case 1:
                costAndSell[i].cost=costAndSell[i-2].cost+sellCost[1];
                costAndSell[i].lastSellYear=i-2;
                break;
            case 2:
                costAndSell[i].cost=costAndSell[i-3].cost+sellCost[2];
                costAndSell[i].lastSellYear=i-3;
                break;
        }
    }
    sellYear=costAndSell[lastYear].lastSellYear;
    printf("sellAt[%d], cost[%d]\n", lastYear, costAndSell[lastYear].cost );

    do{
            sellYear=costAndSell[sellYear].lastSellYear;
            printf("sellAt[%d], cost[%d]\n", sellYear, costAndSell[sellYear].cost );
    } while( sellYear>0 );
}

void main(int argc, char * argv[]){
    int lastYear;
    initSellCost();
    lastYear=atoi(argv[1]);
    findBestPath(lastYear);
}

输出结果为:

sellAt[5], cost[228000]
sellAt[3], cost[176000]
sellAt[0], cost[100000]

答案 3 :(得分:0)

您提供的链接是动态编程 - 代码非常易于阅读。我建议你仔细看看代码,看看它在做什么。

答案 4 :(得分:0)

以下方法可能有用。

每年年底,您有两种选择。您可以选择交易或选择支付维护成本一年。除了第3年,你没有选择。你必须交易肯定。

这可以通过递归方法解决,您可以选择交易中的最低成本,而不是在特定年份进行交易。

表可以维持抵消,第n年(无交易),以便不需要重新计算值

答案 5 :(得分:0)

我认为它不能使用动态编程,因为我找不到最佳子结构和重叠子问题。 N年的成本取决于N-1,N-2和N-3年的行为。很难找到最佳的子结构。