COCI2014/2015 Contest#1 D MAFIJA(樹形DP/貪心)
阿新 • • 發佈:2018-11-06
題意
給定一個
個節點的圖,每個點有且僅有一條出邊,選取最多的點使得沒有邊連線相鄰兩個選中點。
思路
把圖當無向圖看,就是求無向圖中的最大獨立集。但這張圖不是一般的圖,它是一棵“基環內向樹”,如下圖所示:
這種圖最明顯的特徵就是每個點只有一條出邊,根據這個性質,每個點按邊的指向
,總能遇到一個環,基環外向樹則反之。而對於一個連通塊而言,拆去環上的一條邊,形成的無向圖是一棵樹。這些性質為樹形
和貪心提供了條件。
樹上的最大獨立集就是簡單的“取與不取”的轉移,想要求解必須要轉化成樹的問題。我們可以通過分析答案來決定決策(終態分析),任意拆去環上的一條邊
最後的答案中
中至少有一個點不取,那我們斷去
邊後,以
為根,以
為根分別
一次,分別取
,再取
。最後把所有連通塊的答案相加即可。其中因為只需任意拆一條邊,也只用拆一條邊,可以在並查集並邊的時候,不連線將會成環的邊,並存下這條邊即可。這種寫法在基環樹轉一般樹的寫法中有拓展性。
貪心的寫法正確性不是那麼顯然。首先對於環外的情況,肯定是挑入度為零的點,可以證明假設挑入度更大的點沒有入度為零的點優。那就可以按照這個步驟隔一個的挑點。假設標到了環上的點,那就可以直接把環剖開了,最後把沒剖開的環重新剖一次。只需要用一個
陣列去標記遍歷過的點,然後傳一個引數表示這個點選不選,同時統計答案。
程式碼
樹形DP
#include<bits/stdc++.h>
#define FOR(i,x,y) for(int i=(x),i##END=(y);i<=i##END;++i)
#define DOR(i,x,y) for(int i=(x),i##END=(y);i>=i##END;--i)
typedef long long LL;
using namespace std;
const int N=1e5+3;
template<const int maxn,const int maxm>struct Linked_list
{
int head[maxn],to[maxm],nxt[maxm],tot;
void clear(){memset(head,-1,sizeof(head));tot=0;}
void add(int u,int v){to[++tot]=v,nxt[tot]=head[u],head[u]=tot;}
#define EOR(i,G,u) for(int i=G.head[u];~i;i=G.nxt[i])
};
Linked_list<N,N<<1>G;
int fa[N],n;
int getfa(int k){return k==fa[k]?k:fa[k]=getfa(fa[k]);}
bool mer(int x,int y)
{
int fx=getfa(x),fy=getfa(y);
if(fx==fy)return 0;
fa[fx]=fy;
return 1;
}
int To[N],dp[N][2],ans;
int ra[N],rb[N],cnt;
void dfs(int u,int f)
{
dp[u][0]=0,dp[u][1]=1;
EOR(i,G,u)
{
int v=G.to[i];
if(v==f)continue;
dfs(v,u);
dp[u][0]+=max(dp[v][0],dp[v][1]);
dp[u][1]+=dp[v][0];
}
}
int main()
{
G.clear();
int n;
scanf("%d",&n);
FOR(i,1,n)fa[i]=i;
FOR(i,1,n)
{
scanf("%d",&To[i]);
if(mer(i,To[i]))
{
G.add(i,To[i]);
G.add(To[i],i);
}
else cnt++,ra[cnt]=i,rb[cnt]=To[i];
}
FOR(i,1,cnt)
{
dfs(ra[i],0);
int maxer=dp[ra[i]][0];
dfs(rb[i],0);
maxer=max(maxer,dp[rb[i]][0]);
ans+=maxer;
}
printf("%d\n",ans);
return 0;
}
貪心
#include<bits/stdc++.h>
#define FOR(i,x,y) for(int i=(x),i##END=(y);i<=i##END;++i)
#define DOR(i,x,y) for(int i=(x),i##END=(y);i>=i##END;--i)
typedef long long LL;
using namespace std;
const int N=1e5+3;
int To[N],ind[N],n,ans;
bool vis[N];
void dfs(int u,bool kl)
{
if(vis[u])return;
vis[u]=1;
ans+=kl;
ind[To[u]]--;
if(!ind[To[u]]||kl)dfs(To[u],!kl);
}
int main()
{
scanf("%d",&n);
FOR(i,1,n)scanf("%d",&To[i]),ind[To[i]]++;
FOR(i,1,n)if(!ind[i])dfs(i,1);
FOR(i,1,n)dfs(i,0);
printf("%d\n",ans);
return 0;
}