/* 规定某些商品不能同时购买,而且每种商品只能买一件。如何获得最大优惠。 输入格式: 第一行两个整数K,M(1<=K<=1000),K表示商品数量,K中商品的编号一次为1到K。 接下来K行,第i行,表示购买商品K能够节省的金额。 在接下来M行,每行有两个整数A和B,表示A,B不能同时购买。 */ /* 分析:本题的关键是构造一个节点带全的图,结果表示商品,节点权值表示商品优惠 金额,边表示边所连的两个点的商品不能同时购买。 题目就可以转化为在图中找出一个点集,该点集中的任何两点没有边连着而且其 权值之和最大。依题意,该图是无环图,所以该图有若干棵树组成。只要把每棵 树的最大权值加起来就可以了。 对于单棵树: (1)对该树进行深度或广度遍历生成相应的有向树。 (2)对树中的每个结点定义两个函数F(i)和G(i),分别表示以i为根的子树中选择i 时的最大权值和不选择i时的最大权值和。再加一个S(i)表示i结点的权值。 动态规划方程表示为: 对于叶子结点:F(i)=S(i),G(i)=0 对于中间结点:F(i)=sum(G(j)+S(i)) ,G(i)=sum(max(F(j),G(j))) j为i的孩子 (3)若该树的根为r,则该树的最大权值和为max(F(r),G(r)) */ #include <stdio.h> #include <string.h> #define M 1001 int k,S[M]; struct node { int value; node *next; node(int v=0,node *p=0 ){ value = v; next = p; } }*g[M]; /* g[i]是有第几个结点连接的所有结点组成的队列的队列头指针用以记录当前已经计算过的结点, 若是1,则已被考虑过,0表示还没有考虑 */ int mark[M]; long F[M],G[M]; //记录动态规划的函数值 /*深度遍历生成相应的有向树*/ void search(int v) { mark[v] = 1; for(node* loop = g[v];loop;loop=loop->next) if( mark[loop->value] == 0){ node *pre =0,*temp; for(temp = g[loop->value]; temp;temp = temp->next){ if(temp->value==v) break; pre = temp; } if(temp){ if(pre==0){ g[loop->value]= g[loop->value]->next; }else{ pre->next= temp->next; } } search(loop->value); } } long getg(int); /*取v点时,以v为根的树的权值和*/ long getf(int v){ if(F[v]>=0) return F[v]; F[v]=S[v]; for(node * loop=g[v];loop;loop=loop->next) F[v]+=getg(loop->value); return F[v]; } /*不取v点时,以v为根的树的权值和*/ long getg(int v){ if(G[v]>=0) return G[v]; G[v] =0; for(node * loop=g[v];loop;loop=loop->next) { long a = getf(loop->value); long b = getg(loop->value); G[v] +=(a>b? a:b); } return G[v]; } int main(){ freopen("input.txt","r",stdin); int m,i,v1,v2; long ans=0; memset(g,0,sizeof(g)); memset(mark,0,sizeof(mark)); for(i=1;i<=1000;i++) { F[i]=G[i]=-1; } scanf("%d%d",&k,&m); for(i=1;i<=k;i++) { scanf("%d",&S[i]); } for(i=1;i<=m;i++) { scanf("%d%d",&v1,&v2); g[v1] = new node(v2,g[v1]); g[v2] = new node(v1,g[v2]); } for(i=1;i<=k;i++) if(mark[i] == 0) { search(i); ans+=(getf(i)>getg(i)? getf(i):getg(i)); } printf("%ld/n",ans); return 0; } 测试数据: 3 11 231 2 结果: 5