BZOJ 1040: [ZJOI2008]骑士
1040: [ZJOI2008]骑士
Description
Z国的骑士团是一个很有势力的组织,帮会中汇聚了来自各地的精英。他们劫富济贫,惩恶扬善,受到社会各
界的赞扬。最近发生了一件可怕的事情,邪恶的Y国发动了一场针对Z国的侵略战争。战火绵延五百里,在和平环境
中安逸了数百年的Z国又怎能抵挡的住Y国的军队。于是人们把所有的希望都寄托在了骑士团的身上,就像期待有一
个真龙天子的降生,带领正义打败邪恶。骑士团是肯定具有打败邪恶势力的能力的,但是骑士们互相之间往往有一
些矛盾。每个骑士都有且仅有一个自己最厌恶的骑士(当然不是他自己),他是绝对不会与自己最厌恶的人一同出
征的。战火绵延,人民生灵涂炭,组织起一个骑士军团加入战斗刻不容缓!国王交给了你一个艰巨的任务,从所有
的骑士中选出一个骑士军团,使得军团内没有矛盾的两人(不存在一个骑士与他最痛恨的人一同被选入骑士军团的
情况),并且,使得这支骑士军团最具有战斗力。为了描述战斗力,我们将骑士按照1至N编号,给每名骑士一个战
斗力的估计,一个军团的战斗力为所有骑士的战斗力总和。
Input
第一行包含一个正整数N,描述骑士团的人数。接下来N行,每行两个正整数,按顺序描述每一名骑士的战斗力
和他最痛恨的骑士。
Output
应包含一行,包含一个整数,表示你所选出的骑士军团的战斗力。
Sample Input
10 2
20 3
30 1
Sample Output
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) {;}
作者:乌鸦坐飞机
出处:http://www.cnblogs.com/whistle13326/
新的风暴已经出现
怎么能够停止不前
穿越时空 竭尽全力
我会来到你身边
微笑面对危险
梦想成真不会遥远
鼓起勇气 坚定向前
奇迹一定会出现