zoukankan      html  css  js  c++  java
  • 【环套树+树形dp】Bzoj1040 [ZJOI2008] 骑士

    Description

    Z国的骑士团是一个很有势力的组织,帮会中汇聚了来自各地的精英。他们劫富济贫,惩恶扬善,受到社会各界的赞扬。最近发生了一件可怕的事情,邪恶的Y国发动了一场针对Z国的侵略战争。战火绵延五百里,在和平环境中安逸了数百年的Z国又怎能抵挡的住Y国的军队。于是人们把所有的希望都寄托在了骑士团的身上,就像期待有一个真龙天子的降生,带领正义打败邪恶。骑士团是肯定具有打败邪恶势力的能力的,但是骑士们互相之间往往有一些矛盾。每个骑士都有且仅有一个自己最厌恶的骑士(当然不是他自己),他是绝对不会与自己最厌恶的人一同出征的。战火绵延,人民生灵涂炭,组织起一个骑士军团加入战斗刻不容缓!国王交给了你一个艰巨的任务,从所有的骑士中选出一个骑士军团,使得军团内没有矛盾的两人(不存在一个骑士与他最痛恨的人一同被选入骑士军团的情况),并且,使得这支骑士军团最具有战斗力。为了描述战斗力,我们将骑士按照1至N编号,给每名骑士一个战斗力的估计,一个军团的战斗力为所有骑士的战斗力总和。

    Solution

    如果不会形成环那么就是经典的最大独立集问题。

    但因为n个点n条边,所以只会有一个环,于是很好讨论。

    每次找出一个连通块以及让这个连通块形成环的边(u,v),dp两次,分别强制不选u和v,这样就排出了这条边的影响,普通dp就行了。

    思路很清楚,不过感觉我做的树形dp还是太少了,不够熟QwQ

    Code

     1 #include<cstdio>
     2 #include<algorithm>
     3 #include<cstring>
     4 #define ll long long
     5 using namespace std;
     6 const int maxn=1e6+5;
     7 
     8 int c[maxn],n;
     9 int head[maxn],e[maxn*2],nxt[maxn*2],k;
    10 int adde(int u,int v){
    11     e[++k]=v;nxt[k]=head[u];head[u]=k;
    12     e[++k]=u;nxt[k]=head[v];head[v]=k;
    13 }
    14 
    15 int dfn[maxn];
    16 int r1,r2,xx;
    17 int cir(int p,int u){
    18     dfn[u]=1;
    19     for(int i=head[u];i;i=nxt[i]){
    20         int v=e[i];
    21         if(v==p) continue;
    22         if(dfn[v]){
    23             r1=u,r2=v;
    24             xx=i;
    25         }
    26         else cir(u,v);
    27     }
    28 }
    29 
    30 int dy(int x){
    31     if(x%2==1) return x+1;
    32     else  return x-1;
    33 }
    34 
    35 ll f[maxn][2];
    36 int vis[maxn];
    37 int dp(int u){
    38     vis[u]=1;
    39     f[u][1]=c[u];
    40     for(int i=head[u];i;i=nxt[i]){
    41         if(i==xx||dy(i)==xx) continue;
    42         int v=e[i];
    43         if(!vis[v]){
    44             dp(v);
    45             f[u][0]+=max(f[v][1],f[v][0]);
    46             f[u][1]+=f[v][0];
    47         }
    48     }
    49 }
    50 
    51 int main(){
    52     scanf("%d",&n);
    53     int to;
    54     for(int i=1;i<=n;i++){
    55         scanf("%d%d",&c[i],&to);
    56         adde(i,to);
    57     }
    58     
    59     ll ansx=0,ans=0;
    60     for(int i=1;i<=n;i++)
    61         if(!dfn[i]){
    62             cir(0,i);
    63             memset(vis,0,sizeof(vis));
    64             memset(f,0,sizeof(f));
    65             dp(r1); ansx=f[r1][0];
    66             
    67             memset(vis,0,sizeof(vis));
    68             memset(f,0,sizeof(f));
    69             dp(r2); ansx=max(ansx,f[r2][0]);
    70             ans+=ansx;
    71         }
    72         
    73     printf("%lld
    ",ans);
    74     return 0;
    75 }
    View Code
  • 相关阅读:
    循环排序总结
    # 区间合并总结
    快慢指针
    #双指针总结
    滑动窗口总结
    leetcode 第 221 场周赛
    剑指 Offer 07. 重建二叉树
    leetcode 406. 根据身高重建队列
    [JLOI2014]松鼠的新家 T22 D71
    软件包管理器 T21 D71
  • 原文地址:https://www.cnblogs.com/xkui/p/4556212.html
Copyright © 2011-2022 走看看