BZOJ 1040: [ZJOI2008]骑士

1040: [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

HINT

 

N ≤ 1 000 000,每名骑士的战斗力都是不大于 1 000 000的正整数。

Source

首先 有n个点 n条边  就有可能是基环树 由于题目没有保证两点之间不一定有边 所以可能是基环树森林

对于没棵基环树 可以跑一边DP   统计Σans

若是一棵树 很容易解决

dp[0][parent]=max(dp[0][son],dp[1][son])

dp[1][parent]=dp[0][son]

其中dp[0][u]表示以点u为根的子树不选u点时的最大权值,dp[1][u]表示以点u为根的子树必选u点时的最大权值。

但显然基环树和普通的树不一样 他可能有环 所以我们可以考虑断砍一条边 跑dp

假设将要删去的环上的边为Ei,边上两端点为u,v。考虑将点u作为新树的根,作树规将得到dp[0][u]和dp[1][u]。由于作树规假设边Ei不存在,则dp[1][u]作为必须u时的最大权值,可能是同时选择点v得到的最大值。在实际图中,u与v并不能同时取到,故只能将dp[0][u]暂时作为答案保存。很显然,这个答案并不能保证是最优的,因为可能最优的答案要包括u(例如其余N-1个点权值val均为1,点u权值为

​ 如何解决不能取到u的问题?再以点v作为新树的根,再做一遍树规,取上次暂存答案与dp[0][v]求最大值即可。

PROBLEM:是否能够保证所求答案必然为题中最大权值?

无向树可以以任意一点u为根,做树形dp求最大值,其答案将保存在dp[0][u]和dp[1][u]中。基环树不考虑dp[1][u]的值,则答案将保存在dp[0][u]中,此时,已遍历所有情况的最优值——除了必须选择点u的情况。将需删除的边的另一端点作为根求值,此时考虑了选与不选u的情况。

同时由于不能同时选择u与v,则答案必然为可行方案的最大值。

 

 1 #include <cctype>
 2 #include <cstdio>
 3 #include <vector>
 4 
 5 using namespace std;
 6 
 7 typedef long long LL;
 8 
 9 const int MAXN=1000010;
10 
11 int n,m,First,Second,id,edge;
12 
13 int val[MAXN];
14 
15 LL dp[2][MAXN];
16 
17 std::vector<pair<int,int> > Graph[MAXN];
18 
19 bool vis[MAXN];
20 
21 inline void read(int&x) {
22     int f=1;register char c=getchar();
23     for(x=0;!isdigit(c);c=='-'&&(f=-1),c=getchar());
24     for(;isdigit(c);x=x*10+c-48,c=getchar());
25     x=x*f;
26 }
27 
28 void DFS(int u,int fa) {
29     vis[u]=true;
30     for(int i=0;i<Graph[u].size();++i) {
31         int v=Graph[u][i].first;
32         if(v==fa) continue;
33         if(!vis[v]) DFS(v,u);
34         else {
35             edge=Graph[u][i].second;
36             First=u;
37             Second=v;
38         }
39     }
40     return;
41 }
42 
43 void DP(int u,int fa) {
44     dp[0][u]=0;
45     dp[1][u]=val[u];
46     for(int i=0;i<Graph[u].size();++i) {
47         int v=Graph[u][i].first;
48         if(v==fa) continue;
49         if(Graph[u][i].second==edge||Graph[u][i].second==(edge^1)) continue;
50         DP(v,u);
51         dp[0][u]+=dp[0][v]>dp[1][v]?dp[0][v]:dp[1][v];
52         dp[1][u]+=dp[0][v];
53     }
54     return;
55 }
56 
57 int hh() {
58     read(n);
59     for(int x,i=1;i<=n;++i) {
60         read(val[i]);read(x);
61         Graph[i].push_back(make_pair(x,id++));
62         Graph[x].push_back(make_pair(i,id++));
63     }
64     LL t,ans=0;
65     for(int i=1;i<=n;++i) {
66         if(vis[i]) continue;
67         DFS(i,-1);
68         DP(First,-1);
69         t=dp[0][First];
70         DP(Second,-1);
71         ans+=dp[0][Second]<t?t:dp[0][Second];
72     }
73     printf("%lld\n",ans);
74     return 0;
75 }
76 
77 int sb=hh();
78 int main(int aegc,char**argv) {;}
代码

 

posted @ 2017-09-27 19:57  拿叉插猹哈  阅读(111)  评论(0编辑  收藏  举报