算法导论-15-4-计划一个公司聚会
2012-08-31 21:31
459 查看
题目:
思考:
树中每个结点表示一个雇员,为每个结点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); 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; }
相关文章推荐
- 算法导论-15-4-计划一个公司聚会
- 算法导论 思考题 15-6(公司聚会计划)
- 动态规划--计划一个公司聚会
- ACM:计划一个公司聚会 (大一时头疼…
- 动态规划--计划一个公司聚会
- 计划一个公司聚会
- 动态规划--计划一个公司聚会
- 计划一个公司聚会
- 最近在公司闲的一米,给自己定一个计划
- 公司聚会(有一个重点地方)
- 公司计划上线一个联盟推广
- 当一个公司抠门到连年中聚会都取消的时候, 还能指望什么?
- 创业公司增长指南:如何建立,完善和规模化一个增长计划
- 给一个朋友创业公司商业计划书的建议
- 每天一个linux命令(15):tail 命令
- 好书推荐-腾讯方法:一个市值1500亿美元公司的产品真经
- 公司新来了一个漂亮前台,一语引发了大家的集体沉默!
- unity3d使用感触(一个新公司的项目负责人的感触)
- 原型开发计划:用HTML5为BlackBerry 10编写一个移动OA客户端
- 关于IT公司招聘的一个思考