1. 程式人生 > >HYSBZ 1040 騎士 (基環外向樹DP)

HYSBZ 1040 騎士 (基環外向樹DP)

戰爭 同時 one != col 並且 turn 不可 表示

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

Input

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

Output

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

Sample Input3 10 2 20 3 30 1

Sample Output30Hint

N ≤ 1 000 000,每名騎士的戰鬥力都是不大於 1 000 000的正整數。

題目大意:

分別給你n個騎士的戰鬥力和他痛恨的騎士(有且僅有一個且不是自己)。要你組織一個軍團,使得軍團戰鬥力最強,且軍團中沒有一個人是另一個人所痛恨的。軍團戰鬥力極為騎士戰鬥力之和。

看到題目,就想到了之前做到的摘蘋果問題(沒有上司的聚會)。相鄰等級的蘋果不能同時被采摘。

DP表達式即為:dp[i][0]=∑max(dp[son][0],dp[son][1]);dp[i][1]=∑dp[son][0];

但是這道題是可能出現環(環上至少有3個元素)的。

如果出現環,則刪去環中的一條邊,從這條邊的兩點分別做DP,最後答案便是max(dp[st][0],dp[en][0]),即兩點不可能同時取到。

此題可能有多個連通分量,且並非所有連通分量都一定有環(有互相痛恨的騎士)(這導致一條無向邊可能存儲了兩次,要刪去一個),這些在代碼中都要註意。

答案要lol,鏈式前向星要開雙倍數組!

技術分享圖片
#include<cstdio>
#include<queue>
#include
<cstring> #include<algorithm> using namespace std; const int maxn=1000000; int fight[maxn+5]; int to[maxn*2+5]; int nex[maxn*2+5]; int head[maxn+5]; int cnt; void addedge(int u,int v) { to[cnt]=v; nex[cnt]=head[u]; head[u]=cnt++; } bool isedge(int u,int v) { for(int i=head[u];i!=-1;i=nex[i]) { if(to[i]==v) return true; } return false; } int vis1[maxn+5]; int st,en; void dfs1(int x,int fa) { vis1[x]=1; for(int i=head[x];i!=-1;i=nex[i]) { if(to[i]!=fa) { if(!vis1[to[i]]) dfs1(to[i],x); else { st=x; en=to[i]; } } } } long long dp[maxn+5][2]; int vis2[maxn+5]; void dfs2(int x) { vis2[x]=1; bool flag=false; for(int i=head[x];i!=-1;i=nex[i]) { if(!vis2[to[i]]&&!((x==st&&to[i]==en)||(x==en&&to[i]==st))) { dfs2(to[i]); flag=true; dp[x][1]+=dp[to[i]][0]; dp[x][0]+=max(dp[to[i]][0],dp[to[i]][1]); } } dp[x][1]+=1LL*fight[x]; if(!flag) dp[x][1]=1LL*fight[x]; } int main() { int n; while(scanf("%d",&n)!=EOF) { memset(head,-1,sizeof(head)); cnt=0; for(int i=1,a,b;i<=n;i++) { scanf("%d%d",&a,&b); fight[i]=a; if(!isedge(i,b)) { addedge(i,b); addedge(b,i); } } long long ans=0; memset(vis1,0,sizeof(vis1)); for(int i=1;i<=n;i++) { if(!vis1[i]) { st=-1,en=-1; dfs1(i,-1); //printf("%d %d\n",st,en); if(st==-1&&en==-1) { memset(vis2,0,sizeof(vis2)); memset(dp,0,sizeof(dp)); dfs2(i); ans+=max(dp[i][0],dp[i][1]); } else { memset(vis2,0,sizeof(vis2)); memset(dp,0,sizeof(dp)); dfs2(st); long long temp=dp[st][0]; memset(vis2,0,sizeof(vis2)); memset(dp,0,sizeof(dp)); dfs2(en); //printf("%d %d\n",temp,dp[en][0]); ans+=max(temp,dp[en][0]); } } } printf("%lld\n",ans); } return 0; }
View Code

HYSBZ 1040 騎士 (基環外向樹DP)