[HNOI2003]消防局的设立

题目链接:https://www.luogu.org/problemnew/show/P2279

解析部分请看这位大佬的博客:https://www.luogu.org/blog/contributation/solution-p2279

这道题目的代码:

//核心是dis数组,即dis[i]表示距离i节点最近的消防站的距离
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;

const int N=1e3+10, INF=0x3f3f3f3f;
int n, ans, d[N], f[N], seq[N], dis[N];

bool cmp(int a, int b) {
    return d[a]>d[b];
}

int main() {
    memset(dis, INF, sizeof(dis));
    cin >> n;
    seq[1]=1;
    for (int i=2; i<=n; i++) {
        cin >> f[i];
        d[i]=d[f[i]]+1;
        seq[i]=i;
    }
    sort(seq+1, seq+n+1, cmp);按照深度由大到小间接排序
    for (int i=1; i<=n; i++) {
        int cur=seq[i], last=f[cur], lasts=f[last];//每次选取深度最大
        dis[cur]=min(dis[cur], min(dis[last]+1, dis[lasts]+2));//维护dis数组
        if (dis[cur]>2) {//如果它无法被覆盖到
            dis[lasts]=0;//那么在它的父亲的父亲上设立消防站
            ans++;
            dis[f[lasts]]=min(dis[f[lasts]], 1), dis[f[f[lasts]]]=min(dis[f[f[lasts]]], 2);//维护dis数组
        }
    }
    cout << ans;
    return 0;
}
//理解这段代码要从在父亲的父亲设站开始,因为事实上这才是我们想要做的开始的地方,之后只需向上维护dis数组,其余节点会在每次选深度最大的那一步进行更新

ps:另外请区分树的最大独立集和最小覆盖,最小覆盖强调的是将整幅图覆盖用最少的点覆盖,而最大独立集则是按照该点选了,则它的子节点和父节点一定不能选,如果不选,则子节点和父节点按照题目进行选择。https://www.luogu.org/problemnew/show/P1352可将这个题和上面消防局那个题进行对比,来进行更好的理解。

关于大佬说的普适性,这里给出最小k覆盖的代码:

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;

const int N=1e3+10, INF=0x3f3f3f3f;
int n, k, ans, cnt, d[N], f[N], ancestor[N], seq[N], dis[N];
//用ancestor数组来存向上k距离的各个节点

bool cmp(int a, int b) {
	return d[a]>d[b];
}

void update(int cur) {
	memset(ancestor, 0, sizeof(ancestor));
	ancestor[0]=cur;
	for (int i=1; i<=k; i++) {
		if (f[ancestor[i-1]]==0) break;
		ancestor[i]=f[ancestor[i-1]];
	}
	return ;
}

int main() {
	memset(dis, INF, sizeof(dis));
	cin >> n;
	seq[1]=1;
	k=2;
	for (int i=2; i<=n; i++) {
		cin >> f[i];
		d[i]=d[f[i]]+1;
		seq[i]=i;
	}
	sort(seq+1, seq+n+1, cmp);
	for (int i=1; i<=n; i++) {
		update(seq[i]);
		for (int j=1; j<=k; j++) {
			if (ancestor[j]==0) break;
			dis[ancestor[0]]=min(dis[ancestor[0]], dis[ancestor[j]]+j);
		}
		if (dis[ancestor[0]]>k) {
			dis[ancestor[k]]=0;
			ans++;
			update(ancestor[k]);
			for (int j=1; j<=k; j++) {
				if (ancestor[j]==0) break;
				dis[ancestor[j]]=min(dis[ancestor[j]], j);
			}
		}
	}
	cout << ans;
	return 0;
}
//复杂度O(nk)

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值