1. 程式人生 > 實用技巧 >[HNOI2003][樹上貪心]消防局的設立

[HNOI2003][樹上貪心]消防局的設立

(題面)[https://www.luogu.com.cn/problem/P2279]

這道題可以打樹上 \(DP\)但顯然貪心方便
我們貪心地考慮這個問題,一個葉子結點的消防站必然是建在它的爺爺上,才能儘可能多的增加覆蓋的點,所以先跑一遍 \(DFS\) 處理深度,再按深度降序掃描節點即可。
時間複雜度:\(O(n^2)\),可以通過本題。
程式碼:

// no greater than 2 -> father and grandfather

# include <iostream>
# include <cstdio>
# include <queue>
# define sp std::pair
# define mp std::make_pair
# define MAXN 1005

struct edge{
	int v, next;
}e[MAXN<<1];
struct node{
	int dep, fa;
}nd[MAXN];
int hd[MAXN], cntE;
bool vis[MAXN];
std::priority_queue<sp<int, int>, std::vector<sp<int, int> >, std::less<sp<int, int> > >Q;
// pair<dep, id>

void AddE(int u, int v);
void PreDFS(int now, int fa);
void AnsDFS(int now, int dis);

int main(){
	int n, ans = 0;
	scanf("%d", &n);

	for(int i = 2, x; i <= n; i++){
		scanf("%d", &x);
		AddE(i, x); AddE(x, i);
	}

	PreDFS(1, 0);

	int now = Q.top().second;
	while(Q.size()){
		while(vis[now = Q.top().second] && Q.size()){
			Q.pop();
		}

		if(Q.empty()){
			break;
		}

		if(nd[nd[now].fa].fa){
			AnsDFS(nd[nd[now].fa].fa, 0);
		}
		else{
			AnsDFS(1, 0);
		}

		ans++;
	}

	printf("%d", ans);

	return 0;
}

void AnsDFS(int now, int dis){
	if(dis > 2){
		return;
	}
	vis[now] = 1;

	for(int i = hd[now]; i; i = e[i].next){
		AnsDFS(e[i].v, dis+1);
	}
}

void PreDFS(int now, int fa){
	nd[now] = (node){nd[fa].dep+1, fa};

	Q.push(mp(nd[now].dep, now));

	for(int i = hd[now]; i; i = e[i].next){
		if(e[i].v == fa){
			continue;
		}
		PreDFS(e[i].v, now);
	}
}

void AddE(int u, int v){
	e[++cntE] = (edge){v, hd[u]};
	hd[u] = cntE;
}