1. 程式人生 > >公司聚會喜歡程度計算 演算法(動態規劃)Dynamic Programming

公司聚會喜歡程度計算 演算法(動態規劃)Dynamic Programming

問題:

     Professor Stewart is consulting for the president of a corporation that is planning a company party. The company has a hierarchical structure; that is, the supervisor relation forms a tree rooted at the president. The personnel office has ranked each employee with a conviviality rating, which is a real number. In order to make the party fum for all attendees, the president does not want both an employee and his or her immediate supervisor to attend.

     Professor Stewart is given the tree that describes the structure of the corporation, using theleft-child, right-sibling representation. Each node of the tree holds, in addition to the pointers, the name of an employee and that employee’s conviviality ranking. Describe an algorithm to make up a guest list that maximizes the sum of the conviviality ratings of the guests. Analyze the running time of your algorithm.


Stewart教授是一家公司總裁的顧問,這家公司計劃一個公司聚會。這個公司有一個層次式的結構;也就是說,管理關係形成一棵以總裁為根的樹。人事部給每個僱員以喜歡聚會的程度來排名,這是個實數。為了使每個參加者都喜歡這個聚會,總裁不希望一個僱員和他(她)的直接上司同時參加。

   Stewart教授面對一棵描述公司結構的樹,使用了左子女、右兄弟表示法。樹中每個結點除了包含指標,還包含僱員的名字和該僱員喜歡聚會的排名。描述一個演算法,它生成一張客人列表,使得客人喜歡聚會的程度的總和最大。分析你的演算法的執行時間

答:

      分析:求出以每個節點為根節點的子樹去或者不去的最大喜歡程度和,以此往上推,本題就是求根節點去或者不去的最大喜歡程度

。顯然,這具有最優子序列結構。

     給每個節點增加兩個域,select,unsel,select[i]表示i節點去時,以i節點為根的子樹的喜歡程度和,unsel[i]表示i節點不去時,所獲得的喜歡程度和

    公式如下:

    r.select=r.data+sum(unsel[j]);          //jr的孩子,r.data為節點r的喜歡程度

    r.unsel=sum(max{unsel[j],select[j]});

   當i選擇去時,它的孩子節點肯定不能去;當i不去時,其孩子節點可以選擇去或者不去;

   然後根據求出的根節點的最大喜歡程度和,找出參加的名單。

首先是計算每個節點為根的子樹的最大喜歡程度和。

typedef struct Node

{

    int data;//表示喜歡聚會的程度

    int *parent,*lchild,*rsibling;//左孩子,右兄弟結構

    int select,unsel;//這兩個值在初始化樹時,均為零

    int go;//標識當前節點是否參加聚會

}Node;

typedef struct stack{

   int *top,*base;

   int stacksize;

}stack;//為了方便說明,

void traverse(Node*T)

{

  stack s;

  s.top=-1;

  Node *r;

  r=T;

  while(top!=-1||r!=NULL)

  {

    s.top++;

    *s.top=r;

    while(r.lchild!=NULL){//檢查r節點是否有左孩子,有則進棧

      s.top++;

      *s.top=r.lchild;

      r=r.lchild;

    }

    r=*s.top;//出棧

    s.top--;

//檢查r是否為葉節點

    if(r.lchild==NULL)

    {//r為葉節點

      r.select=r.data;

      r.unsel=0;

    }

    else

    {//r不是葉節點,若以r為根節點,則還要考慮孩子和其兄弟的喜歡程度

      r.select=r.data+sum(j.unsel);//j為r的孩子

      r.unsel=sum(max{j.unsel,j.select});

    }

    r=r.rightsibling;//指向r節點的右兄弟

  }

}

其次是找出名單。

void find_the_list(Node*T){

   Node *r,*s;

   r=T;

   if(r==NULL) return;

   else if(r.parent==NULL){//表示是根節點

   if(r.select>r.unsel) r.go=1;//表示選擇參加

   else r.go=0;//表示不參加

  }

  else{

    if(r.parent.go=1) r.go=0;

    else//當前節點的直接上司不會參加,即當前節點可以選擇參加或者不參加,根據喜好程度和

      if(r.unsel<r.select) r.go=0;

      elser.go=1;

  }

   r=r.lchild;

   s=r.rsibling;

   find_the_list(r);

   find_the_list(s);

}

最後輸出go域為1的節點,即公司的聚會名單,演算法時間為O(2n),n為節點數。