算法导论-15-4-计划一个公司聚会
2016-06-13 00:00
489 查看
题目:
思考:
树中每个结点表示一个雇员,为每个结点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])即所求的值
代码:
思考:
树中每个结点表示一个雇员,为每个结点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] = 0 p->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); 3ff0 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 1 1 2 1 3 3 4 3 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; }
相关文章推荐
- 算法导论-13-1-持久动态集合
- Linux2.6为数据结构分配内存-slab
- 算法导论-15-7-达到最高效益的调度
- 算法导论 第10章 10.4 有根树的表示
- 算法导论 第13章 红黑树
- 算法导论 第7章 快速排序
- 算法导论 第12章 二叉查找树
- 算法导论-24.2-有向无回路图中的单源最短路径
- 算法导论 第10章 10.1 栈和队列
- 算法导论 11.1-4 大数组的直接寻址表
- 算法导论-14-1-最大重叠点
- 算法导论-12-2-基数树
- 算法导论-14-2-Josephus排列
- 算法导论-8-3-排序不同长度的数据项
- 算法导论 10.4-2 O(n)时间 递归遍历二叉树
- 算法导论-9.3-3-快速排序-最坏时间O(nlgn)
- 算法导论-24.1-Bellman-Ford算法
- 算法导论-15-1-双调欧几里得旅行商问题
- 算法导论-13-3-AVL树
- 算法导论 第22章 图算法 22.2 广度优先搜索