1040: [ZJOI2008]骑士
Time Limit: 10 Sec Memory Limit: 162 MB
Submit: 5403 Solved: 2060
[Submit][Status][Discuss]Description
Z国的骑士团是一个很有势力的组织,帮会中汇聚了来自各地的精英。他们劫富济贫,惩恶扬善,受到社会各
界的赞扬。最近发生了一件可怕的事情,邪恶的Y国发动了一场针对Z国的侵略战争。战火绵延五百里,在和平环境
中安逸了数百年的Z国又怎能抵挡的住Y国的军队。于是人们把所有的希望都寄托在了骑士团的身上,就像期待有一
个真龙天子的降生,带领正义打败邪恶。骑士团是肯定具有打败邪恶势力的能力的,但是骑士们互相之间往往有一
些矛盾。每个骑士都有且仅有一个自己最厌恶的骑士(当然不是他自己),他是绝对不会与自己最厌恶的人一同出
征的。战火绵延,人民生灵涂炭,组织起一个骑士军团加入战斗刻不容缓!国王交给了你一个艰巨的任务,从所有
的骑士中选出一个骑士军团,使得军团内没有矛盾的两人(不存在一个骑士与他最痛恨的人一同被选入骑士军团的
情况),并且,使得这支骑士军团最具有战斗力。为了描述战斗力,我们将骑士按照1至N编号,给每名骑士一个战
斗力的估计,一个军团的战斗力为所有骑士的战斗力总和。Input
第一行包含一个正整数N,描述骑士团的人数。接下来N行,每行两个正整数,按顺序描述每一名骑士的战斗力
和他最痛恨的骑士。Output
应包含一行,包含一个整数,表示你所选出的骑士军团的战斗力。
Sample Input
3
10 2
20 3
30 1Sample Output
30HINT
N ≤ 1 000 000,每名骑士的战斗力都是不大于 1 000 000的正整数。
题解
分析题意可以得到这应该是个最大权独立集...
然而普适的最大权独立集问题并不可做...这个问题肯定有特殊的地方. 我们发现每个点主动连出去的边有且仅有一条, 我们可以发现这是个环套树.
如果是树的话直接一个 $DP$ 怼上去就可以了, 而对于环套树, 我们可以先进行一遍 $DFS$ 找找是否有环, 没有环直接按树来 $DP$ , 如果有环的话, 任意选一条边断掉转化成树, 标记这条边连接的两个结点, 两个结点中强制某一个不能选, $DP$ 一遍, 换另一个结点强制不选再跑一遍, 在两种情况下取最大值.
断边的过程可以通过维护全局标记实现, 对于与全局标记相等的边直接不走就可以了, 对于强制不选, 则可以在跑一遍 $DP$ 后强行将强制不选的那个结点选中时得到的最大值赋为 $-infty$ 即可
参考代码
1 #include <cstdio> 2 #include <cstring> 3 #include <climits> 4 #include <cstdlib> 5 #include <iostream> 6 #include <algorithm> 7 8 const int MAXN=1e6+10; 9 10 struct Edge{ 11 int from; 12 int to; 13 int ID; 14 Edge* next; 15 }; 16 Edge E[MAXN*2]; 17 Edge* head[MAXN]; 18 Edge* top=E; 19 20 int n; 21 int disableV; 22 int disableE; 23 int cut1,cut2; 24 long long ans,tmp; 25 bool visited[MAXN]; 26 long long val[MAXN]; 27 long long dp[MAXN][2]; 28 29 void DP(int,int); 30 void DFS(int,int); 31 void Initialize(); 32 void Insert(int,int,int); 33 34 int main(){ 35 Initialize(); 36 for(int i=1;i<=n;i++){ 37 if(visited[i]) 38 continue; 39 else{ 40 DFS(i,0); 41 disableV=cut1; 42 DP(i,0); 43 tmp=std::max(dp[i][0],dp[i][1]); 44 disableV=cut2; 45 DP(i,0); 46 tmp=std::max(tmp,std::max(dp[i][0],dp[i][1])); 47 ans+=tmp; 48 } 49 } 50 printf("%lld ",ans); 51 return 0; 52 } 53 54 void DFS(int root,int prt){ 55 visited[root]=true; 56 for(Edge* i=head[root];i!=NULL;i=i->next){ 57 if(i->ID==prt) 58 continue; 59 else if(visited[i->to]){ 60 cut1=root; 61 cut2=i->to; 62 disableE=i->ID; 63 } 64 else 65 DFS(i->to,i->ID); 66 } 67 } 68 69 void DP(int root,int prt){ 70 dp[root][0]=0; 71 dp[root][1]=val[root]; 72 for(Edge* i=head[root];i!=NULL;i=i->next){ 73 if(i->ID==disableE||i->ID==prt) 74 continue; 75 DP(i->to,i->ID); 76 dp[root][0]+=std::max(dp[i->to][0],dp[i->to][1]); 77 dp[root][1]+=dp[i->to][0]; 78 } 79 if(root=disableV) 80 dp[root][1]=LLONG_MIN; 81 } 82 83 void Initialize(){ 84 scanf("%d",&n); 85 int tmp; 86 for(int i=1;i<=n;i++){ 87 scanf("%d%d",val+i,&tmp); 88 Insert(i,tmp,i); 89 Insert(tmp,i,i); 90 } 91 } 92 93 inline void Insert(int from,int to,int ID){ 94 top->to=to; 95 top->ID=ID; 96 top->from=from; 97 top->next=head[from]; 98 head[from]=top++; 99 }