1. 程式人生 > >bzoj1040 基環樹上dp

bzoj1040 基環樹上dp

math bzoj 其中 期待 不存在 clas 數據 nbsp ostream

【bzoj1040】[ZJOI2008]騎士

Description

Z國的騎士團是一個很有勢力的組織,幫會中匯聚了來自各地的精英。他們劫富濟貧,懲惡揚善,受到社會各界的贊揚。最近發生了一件可怕的事情,邪惡的Y國發動了一場針對Z國的侵略戰爭。戰火綿延五百裏,在和平環境中安逸了數百年的Z國又怎能抵擋的住Y國的軍隊。於是人們把所有的希望都寄托在了騎士團的身上,就像期待有一個真龍天子的降生,帶領正義打敗邪惡。騎士團是肯定具有打敗邪惡勢力的能力的,但是騎士們互相之間往往有一些矛盾。每個騎士都有且僅有一個自己最厭惡的騎士(當然不是他自己),他是絕對不會與自己最厭惡的人一同出征的。戰火綿延,人民生靈塗炭,組織起一個騎士軍團加入戰鬥刻不容緩!國王交給了你一個艱巨的任務,從所有的騎士中選出一個騎士軍團,使得軍團內沒有矛盾的兩人(不存在一個騎士與他最痛恨的人一同被選入騎士軍團的情況),並且,使得這支騎士軍團最具有戰鬥力。為了描述戰鬥力,我們將騎士按照1至N編號,給每名騎士一個戰鬥力的估計,一個軍團的戰鬥力為所有騎士的戰鬥力總和。

Input

第一行包含一個正整數N,描述騎士團的人數。接下來N行,每行兩個正整數,按順序描述每一名騎士的戰鬥力和他最痛恨的騎士。

Output

應包含一行,包含一個整數,表示你所選出的騎士軍團的戰鬥力。

Sample Input

3
10 2
20 3
30 1

Sample Output

30
【數據規模】
對於30%的測試數據,滿足N ≤ 10;
對於60%的測試數據,滿足N ≤ 100;
對於80%的測試數據,滿足N ≤ 10 000。
對於100%的測試數據,滿足N ≤ 1 000 000,每名騎士的戰鬥力都是不大於 1 000 000的正整數。

代碼

太難了。。orz wuala

by wulala

這道題題目中貌似是有向邊,實際上想一想就知道是無向的。

因為一個騎士覺得另一個騎士醜他們倆就走不到一起。

所以整個圖實際上是一個無向環套樹森林。

對於每一棵環套樹,先dfs找環,找到環以後斷環為鏈並將斷開的兩個點強制其中一個點為根且不選做一次樹形DP,對另一個點做同樣操作。

取兩次結果最大值加入ans

  1 #include<cstring>
  2 #include<cmath>
  3 #include<iostream>
  4 #include<algorithm>
  5 #include<cstdio>
  6
7 #define N 1000007 8 #define ll long long 9 using namespace std; 10 11 int n,cnt; 12 ll ans,answer; 13 int v[N],fa[N],head[N],to[N],next[N],ez[N],bh[N]; 14 ll dp[N][2],f[N][4]; 15 bool mark[N]; 16 17 void Ins(int a,int b) 18 { 19 cnt++; 20 to[cnt]=b; 21 next[cnt]=head[a]; 22 head[a]=cnt; 23 } 24 void init() 25 { 26 int i,k; 27 scanf("%d",&n); 28 for(i=1;i<=n;i++) 29 {scanf("%d%d",&v[i],&k);Ins(k,i);fa[i]=k;} 30 } 31 void treedp(int x) 32 { 33 dp[x][1]=v[x];dp[x][0]=0; 34 mark[x]=1; 35 int i=head[x]; 36 while(i>0) 37 { 38 treedp(to[i]); 39 dp[x][0]+=max(dp[to[i]][0],dp[to[i]][1]); 40 dp[x][1]+=dp[to[i]][0]; 41 i=next[i]; 42 } 43 } 44 void finaldp() 45 { 46 int i,k; 47 ans=0; 48 f[1][1]=0;f[1][2]=0; 49 f[1][0]=dp[bh[1]][1]; 50 f[1][3]=dp[bh[1]][0]; 51 for(i=2;i<=bh[0];i++) 52 { 53 k=bh[i]; 54 f[i][0]=f[i-1][1]+dp[k][1]; 55 f[i][1]=max(f[i-1][0],f[i-1][1])+dp[k][0]; 56 f[i][2]=f[i-1][3]+dp[k][1]; 57 f[i][3]=max(f[i-1][2],f[i-1][3])+dp[k][0]; 58 } 59 ans=max(f[bh[0]][1],max(f[bh[0]][2],f[bh[0]][3])); 60 } 61 void solve() 62 { 63 int i,k,j,now; 64 for(i=1;i<=n;i++) 65 { 66 if(mark[i])continue; 67 bh[0]=0; 68 k=i; 69 while(!mark[k]) 70 { 71 mark[k]=1; 72 k=fa[k]; 73 ez[fa[k]]=k; 74 } 75 now=k; 76 while(1) 77 { 78 i=head[k]; 79 dp[k][1]=v[k]; 80 while(i>0) 81 { 82 if(to[i]!=ez[k]) 83 {treedp(to[i]); 84 dp[k][0]+=max(dp[to[i]][0],dp[to[i]][1]); 85 dp[k][1]+=dp[to[i]][0];} 86 i=next[i]; 87 } 88 bh[0]++;bh[bh[0]]=k; 89 k=fa[k]; 90 if(k==now) 91 break; 92 } 93 finaldp(); 94 answer+=ans; 95 } 96 printf("%lld",answer); 97 } 98 int main() 99 { 100 init(); 101 solve(); 102 return 0; 103 }

bzoj1040 基環樹上dp