zoukankan      html  css  js  c++  java
  • [ZJOI2008]骑士 题解

    题目背景:

    Z国的骑士团是一个很有势力的组织,帮会中汇聚了来自各地的精英。他们劫富济贫,惩恶扬善,受到社会各界的赞扬。

    最近发生了一件可怕的事情,邪恶的Y国发动了一场针对Z国的侵略战争。战火绵延五百里,在和平环境中安逸了数百年的Z国又怎能抵挡的住Y国的军队。于是人们把所有的希望都寄托在了骑士团的身上,就像期待有一个真龙天子的降生,带领正义打败邪恶。

    骑士团是肯定具有打败邪恶势力的能力的,但是骑士们互相之间往往有一些矛盾。每个骑士都有且仅有一个自己最厌恶的骑士(当然不是他自己),他是绝对不会与自己最厌恶的人一同出征的。

    战火绵延,人民生灵涂炭,组织起一个骑士军团加入战斗刻不容缓!国王交给了你一个艰巨的任务,从所有的骑士中选出一个骑士军团,使得军团内没有矛盾的两人(不存在一个骑士与他最痛恨的人一同被选入骑士军团的情况),并且,使得这支骑士军团最具有战斗力。

    为了描述战斗力,我们将骑士按照1至N编号,给每名骑士一个战斗力的估计,一个军团的战斗力为所有骑士的战斗力总和。(N<=1,000,000,骑士战斗力为不超过1,000,000的正整数)。

    输入格式:

    第一行包括一个正整数N,描述骑士团的人数。
    接下来N行,每行两个正整数,按顺序描述每一名骑士的战斗力和他最痛恨的骑士。

    题解:

      根据题目的描述,每一个连通块里面有且仅有一个环,这些连通块就构成了一个基环树森林于是我们的思路就是,通过dfs找出环,然后对于环上的每一个骑士,我们以他为根,在树上进行dp(当然这个树上不包括环上其他骑士),找出满足要求的最大战斗力,然后再在环上进行dp,环上相邻的两个骑士不能同时出现在骑士团里。
    图解:

    在树上dp,我们设f [ i ][ 0 ]表示不选根节点 i 时能获得的最大战斗力,f [ i ][ 1 ]表示选根节点 i 时能获得的最大战斗力。
    于是状态转移方程为:

    f[i][0]=Σmax(f[j][0],f[j][1]) (j表示i的儿子,下同)
    f[i][1]=Σf[j][0] + w[i](w[i]表示i的战斗力)

    在环上dp是我们要注意,因为是环,所以第一个骑士和最后一个骑士是相邻的,我们解决这个问题的方法是强制选第一个骑士或者最后一个骑士,然后取最大值。
    设g [ i ][ 0] 表示不选第 i 个骑士的最大战斗力,g [ i ][ 1]表示选第i个骑士的最大战斗力。
    于是状态转移方程为:

    g[i][0]=max(g[i-1][0],g[i-1][1])+f[i][0](f的含义与上面一样,下同)
    g[i][1]=g[i-1][0]+f[i][1] 

    然后取max累加到答案中即可;

    附上代码:

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 typedef long long ll;
     4 const int N=1000000;
     5 int ver[2*N+5],nxt[2*N+5],head[N+5],v[N+5],v2[N+5],c[N+5];//c数组储存环上的节点
     6 ll w[N+5],f[N+5][2],g[N+5][2],ans1,ans2;
     7 int n,cnt,tot,st;//由于存节点时tot是累加起来的,所以用st来表示环开始的节点的位置在c中编号
     8 //我习惯加无向边。
     9 void add(int x,int y){
    10     ver[++cnt]=y,nxt[cnt]=head[x],head[x]=cnt;
    11 }
    12 //dfs找出环
    13 bool dfs(int x,int e){
    14     if(v[x]==1){//找到环的“衔接点”
    15         v[x]=2,v2[x]=1,c[++tot]=x;
    16         return 1;
    17     }
    18     v[x]=1;
    19     for(int i=head[x];i;i=nxt[i]){
    20         if(i!=((e-1)^1)+1 && dfs(ver[i],i)){//邻接表成对储存;如果ver[i]在环上的话
    21             if(v[x]!=2){//不是衔接点
    22                 c[++tot]=x,v2[x]=1;//存入c数组,打上标记
    23                 return 1;
    24             }else{
    25                 return 0;
    26             }
    27         }
    28     }
    29     return 0;
    30 }
    31 //树上进行dp
    32 void tree_dp(int x){
    33     f[x][0]=0,f[x][1]=w[x];
    34     for(int i=head[x];i;i=nxt[i]){
    35         int y=ver[i];
    36         if(v2[y]) continue;//防止找到环上其他点
    37         v2[y]=1;//把基环树上所有点都打上v2标记,标记这颗基环树已经跑过
    38         tree_dp(y);
    39         f[x][0]+=max(f[y][0],f[y][1]); 
    40         f[x][1]+=f[y][0];
    41     }
    42 }
    43 
    44 int main(){
    45     scanf("%d",&n);
    46     for(int i=1;i<=n;++i){
    47         scanf("%lld",&w[i]);
    48         int y;
    49         scanf("%d",&y);
    50         add(i,y),add(y,i);
    51     }
    52     for(int i=1;i<=n;++i){
    53         if(!v2[i]){//如果还没跑过这颗基环树
    54             st=tot+1;
    55             dfs(i,0);//找出基环树上的环;
    56             for(int j=st;j<=tot;++j) tree_dp(c[j]);//对环上点进行dp
    57             memset(g,0,sizeof(g));
    58             g[st][0]=g[st][1]=f[c[st]][0];//强制环上第一个骑士不选
    59             for(int j=st+1;j<=tot;++j){
    60                 g[j][0]=max(g[j-1][0],g[j-1][1])+f[c[j]][0];
    61                 g[j][1]=g[j-1][0]+f[c[j]][1];
    62             }
    63             ans1=max(g[tot][0],g[tot][1]);
    64             memset(g,0,sizeof(g));
    65             g[st][0]=g[st][1]=f[c[st]][1],f[c[st+1]][1]=f[c[st+1]][0];//强制第一个选
    66             for(int j=st+1;j<=tot-1;++j){
    67                 g[j][0]=max(g[j-1][0],g[j-1][1])+f[c[j]][0];
    68                 g[j][1]=g[j-1][0]+f[c[j]][1];
    69             }
    70             ans1=max(ans1,max(g[tot-1][0],g[tot-1][1])+f[c[tot]][0]);
    71             ans2+=ans1;//对每一颗基环树累加答案
    72         }
    73     }
    74     printf("%lld",ans2);
    75     return 0;
    76 }
  • 相关阅读:
    Ubuntu下sudo apt-get install vim 失败的解决办法
    电脑突然出现成功连接网络但不能上网、网络受限(解决办法)
    wxWidgets 安装方法(Windows 8.1 + Visual Studio 2013)
    wxWidgets界面开发工具wxFormBuilder的使用
    隐私策略
    MyEclipse 中自定义日期格式
    Debug Assertion Failed!
    p2 弹簧
    p2 关节
    p2 形状
  • 原文地址:https://www.cnblogs.com/Asika3912333/p/11329359.html
Copyright © 2011-2022 走看看