算法导论-15-4-计划一个公司聚会

来源:互联网 发布:itver网络电视 编辑:程序博客网 时间:2024/04/28 16:21

题目:


思考:

树中每个结点表示一个雇员,为每个结点p增加三个域,Enthu[p]的含义:结点p表示的雇员的喜欢值。In[p]的含义:若p参加聚会,以p为根的子树能得到的最大的喜欢值。NotIn[p]的含义:若p不去参加聚会,以p为根的子树能得到的最大的喜欢值。

初始化:若p为一个叶子结点时,In[p] = Enthu[p],NotIn[p] = 0

子问题:In[p] = Enthu[p] + SUM(NotIn[p->child]),NotIn[p] = SUM(MAX(In[p->child], NotIn[p->child]))

计算当前结点的条件是它的所以孩子结点都已经计算出结果,为了方便编程,程序中使用邻接图来表示管理关系树。

Step1:构造管理关系树G(用邻接图表示,从父到孩子的边)

Step2:对管理关系树做转置操作G2(算法导论-22.1-3-有向图的转置),即从孩子到父的边

Step3:对G2求拓扑排序,依照这个排序做DP(算法导论-22.4-5-用队列实现拓扑排序)

Step4:按照Step3的序列,依次求每个点的In[p]和NotIn[p]

Step5:管理关系树的根结点root,即拓扑序列中的最后一个点,MAX(In[root], NotIn[root])即所求的值

代码:

#include <iostream>#include <queue>using namespace std;#define N 5 //点的个数#define M 4 //边的个数int N, M;queue<int> Q;//用于拓扑排序//边结点结构struct Edge{int start;//有向图的起点int end;//有向图的终点Edge *next;//指向同一个起点的下一条边Edge(int s, int e):start(s),end(e),next(NULL){}};//顶点结点结构struct Vertex{int InDegree;//入度,用于拓扑排序int In;//若p参加聚会,以p为根的子树能得到的最大的喜欢值int NotIn;//若p不去参加聚会,以p为根的子树能得到的最大的喜欢值int Enthu;//结点p表示的雇员的喜欢值Edge *head;//指向以该顶点为起点的下一条边Vertex():InDegree(0),head(NULL){}};//图结构struct Graph{Vertex *V[10+1];//N个顶点Graph(){int i;for(i = 1; i <= N; i++)V[i] = new Vertex;}};//插入边void InsertEdge(Graph *G, Edge *E){//如果没有相同起点的边if(G->V[E->start]->head == NULL)G->V[E->start]->head =E;//如果有,加入到链表中else{E->next = G->V[E->start]->head;G->V[E->start]->head = E;}G->V[E->end]->InDegree++;}//转置Graph* Reverse(Graph *G){Graph *ret = new Graph;int i;//遍历图G中的每一条边,以终点为起点,以起点为终点,加入到新图RET中for(i = 1; i <= N; i++){Edge *E = G->V[i]->head;while(E){Edge *e = new Edge(E->end, E->start);InsertEdge(ret, e);E = E->next;}}return ret;}//按照转置图的拓扑序列,依次求每个点的In[p]和NotIn[p]void Sub(Graph *G, Vertex *p){//若p为一个叶子结点时,In[p] = Enthu[p],NotIn[p] = 0p->In = p->Enthu;p->NotIn = 0;Edge *e = p->head;//若p有孩子while(e){//In[p] = Enthu[p] + SUM(NotIn[p->child])p->In = p->In + G->V[e->end]->NotIn;//NotIn[p] = SUM(MAX(In[p->child], NotIn[p->child]))p->NotIn = p->NotIn + max(G->V[e->end]->NotIn, G->V[e->end]->In);e = e->next;}}void DP(Graph *G1, Graph *G2){//对转置图进行拓扑排序while(!Q.empty())Q.pop();int i, ret;for(i = 1; i <= N; i++)if(G2->V[i]->InDegree == 0)Q.push(i);while(!Q.empty()){int q = Q.front();Q.pop();//按照转置图的拓扑序列,依次求每个点的In[p]和NotIn[p]Sub(G1, G1->V[q]);ret = max(G1->V[q]->In, G1->V[q]->NotIn);//用于拓扑排序Edge *e = G2->V[q]->head;while(e){G2->V[e->end]->InDegree--;if(G2->V[e->end]->InDegree == 0)Q.push(e->end);e = e->next;}}//管理关系树的根结点root,即拓扑序列中的最后一个点,MAX(In[root], NotIn[root])即所求的值cout<<ret<<endl;}/*6 2 5 1 11 21 33 43 5*/int main(){while(cin>>N>>M){//构造一个空的图Graph *G = new Graph;int i, start, end;//输入每个人的喜欢程度for(i = 1; i <= N; i++)cin>>G->V[i]->Enthu;//输入边for(i = 1; i <= M; i++){cin>>start>>end;Edge *E = new Edge(start, end);InsertEdge(G, E);}//转置Graph *G2 = Reverse(G);DP(G, G2);}return 0;}


原创粉丝点击