题目描述:
题意:给定每个人的战斗力和它最讨厌的人,组建一个队伍,要求求出使得队伍战斗力总和的最大值。
思路分析:
其实看到这道题我们应该就能联想到我们在之前刚学树形dp时做的一道叫“没有上司的舞会”的题目,这道题和那道题十分相似,只不过这道题可能会出现环的情况,而且每个人都有了权值。
既然我们联想到了我们做过的题,我们就可以用那道题的方法来应对这道题,即树形DP,针对于环的情况,我们可以把环的其中一条边断开,这样就形成了树的结构,但是要注意环断开的两边的两个节点不可以同时被选。注意了以上要素后,我们就可以解决这道题了,我们先找到环,再环要断开的两边进行dp,注意强制让两边的点不能同时在一个组中,用dp[i][0]表示i节点不选,dp[i][1]表示该节点被选,相邻节点不能同时被选,于是我们有:dp[i][0]=sigema(max(dp[i][0],dp[i][1])),dp[i][1]=sigema(dp[i][0]);之后递归统计答案即可。注意开long long。
另外,数组一般真的不要开得太大,我刚开始开了1e8的数组,结果在洛谷上编译都过不去,就出现下图的情况:
所以开数组千万小心。
上代码:
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 using namespace std; 5 const int N=2e6+10; 6 long long ans,dp[N][2]; 7 struct Node{ 8 int next,to; 9 }e[N]; 10 int Head[N],fa[N],vis[N],val[N],tot,root; 11 void Add(int x,int y){ //加边函数 12 e[++tot].to=y; 13 e[tot].next=Head[x]; 14 Head[x]=tot; 15 } 16 void Dp(int x){ //选定起点后开始动规 17 vis[x]=1; 18 dp[x][0]=0;dp[x][1]=val[x]; 19 for(int i=Head[x];i;i=e[i].next){ 20 int v=e[i].to; 21 if(v!=root){ 22 Dp(v); 23 dp[x][0]+=max(dp[v][1],dp[v][0]); 24 dp[x][1]+=dp[v][0]; 25 } 26 else{ //到了删除边的另一端,强制拆开 27 dp[v][1]=-N; 28 } 29 } 30 } 31 void find(int x){ 32 vis[x]=1; 33 root=x; 34 while(!vis[fa[root]]){ //找到环的位置 35 root=fa[root]; 36 vis[root]=1; 37 } 38 Dp(root); //分别跑环中选定边两边的节点,在Dp部分有关于不让他们在一起的判断 39 long long t=max(dp[root][0],dp[root][1]); 40 vis[root]=1; 41 root=fa[root]; 42 Dp(root); 43 ans+=max(t,max(dp[root][0],dp[root][1])); 44 return; 45 } 46 int main(){ 47 int n; 48 scanf("%d",&n); 49 for(int i=1;i<=n;++i){ 50 int x; 51 scanf("%d%d",&val[i],&x); 52 Add(x,i); //让他讨厌的人指向他 53 fa[i]=x; 54 } 55 for(int i=1;i<=n;++i){ 56 if(!vis[i]) find(i); //一个环一个环的查 57 } 58 printf("%lld ",ans); //输出答案 59 return 0; 60 }