問題 L: 開會--樹的最小支配集
阿新 • • 發佈:2018-11-12
問題 L: 開會
時間限制: 1 Sec 記憶體限制: 128 MB
提交: 111 解決: 20
[提交] [狀態] [討論版] [命題人:admin]
題目描述
開會,是對所有人時間的浪費,是對集體的謀殺。
山區學校的一些學生之間的關係似乎好得有點過頭,以至於傳出了一些(在風紀委員們看來)不好的緋聞。具體地,有n個學生,n-1條緋聞,每條緋聞的主角都是倆學生。記者們的惡趣味保證任意兩個學生,可以通過若干條緋聞直接或間接地聯絡在一起。
於是學校打算邀請一些學生參加座談會。
校長相信,假如邀請了某位學生x來開會,那麼就能夠震懾到x本人,以及和x在同一條緋聞裡的學生們。
礦泉水是寶貴的,校長想知道最少需要請多少人來開會,才有可能震懾到所有同學。
輸入
第一行是 n 表示學生數。
之後n-1行,每行倆整數x,y,表示學生x和y之間有緋聞。( x≠y,但不一定x<y )
輸出
一行,一個整數表示最少要邀請多少人。
樣例輸入
5
1 3
5 2
4 3
3 5
樣例輸出
2
提示
可以選擇邀請學生2&3,或者是邀請學生3&5
對於前10%的資料,n<=15
對於前30%的資料,n<=2000
對於接下來30%的資料,n<=10^5,且有倆學生需要通過n-1條緋聞才能扯上關係。
對於前100%的資料,n<=10^5,1<=x,y<=n
居然是板子題,嗯,板子還是很好理解的,可惜當時不知道
無向圖,兩倍的邊,所以邊開兩倍
一遍dfs,
求出某個點的父親,並且將dfs的順序放入棧中(需要反序)
那麼依次彈出棧中點
如果他沒被覆蓋(震懾),那麼標記他父親(這個就是被拉去開會的那個),這樣覆蓋(震懾)掉他,他父親,他父親的父親
棧空即結束
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 1e5+7; struct Edge{ int to,next; }E[maxn<<1];//邊 int Fa[maxn],head[maxn],st[maxn],now,cnt,n;//Fa-father,head-鏈式前向星,st-棧,now-dfs序,cnt-鏈式前向星邊數,n-樹節點數 bool vis[maxn],Set[maxn];//vis是-是否震懾某人,Set是-某人去開會 int ans;//答案 void dfs(int x,int dad){ Fa[x] = dad; st[now++] = x;//入棧 for(int i=head[x];~i;i = E[i].next){ int v = E[i].to; if(v == dad)continue; dfs(v,x); } } void add(int u,int v){ E[cnt] = {v,head[u]}; head[u] = cnt++; E[cnt] = {u,head[v]}; head[v] = cnt++; } void init(){ memset(head,-1,sizeof(head)); memset(Fa,0,sizeof(Fa)); memset(st,0,sizeof(st)); } int main(){ init(); scanf("%d",&n); for(int i=0;i<n-1;i++){ int x,y; scanf("%d%d",&x,&y); add(x,y); } dfs(1,0); while(now){//棧不空 int top = st[--now];//取棧頂(這裡理解為倒序遍歷陣列也行) if(!vis[top]){ if(!Set[Fa[top]]){//選其父親去開會 Set[Fa[top]] = 1; ans ++; } vis[top] = 1;//它 vis[Fa[top]] = 1;//它父親 vis[Fa[Fa[top]]] = 1;//它爺爺 } } printf("%d\n",ans); return 0; }