洛谷2279 消防局的设立
Description
2020年,人类在火星上建立了一个庞大的基地群,总共有n个基地。起初为了节约材料,人类只修建了n-1条道路来连接这些基地,并且每两个基地都能够通过道路到达,所以所有的基地形成了一个巨大的树状结构。如果基地A到基地B至少要经过d条道路的话,我们称基地A到基地B的距离为d。
由于火星上非常干燥,经常引发火灾,人类决定在火星上修建若干个消防局。消防局只能修建在基地里,每个消防局有能力扑灭与它距离不超过2的基地的火灾。
你的任务是计算至少要修建多少个消防局才能够确保火星上所有的基地在发生火灾时,消防队有能力及时扑灭火灾。
Input:
输入文件名为input.txt。
输入文件的第一行为n (n<=1000),表示火星上基地的数目。接下来的n-1行每行有一个正整数,其中文件第i行的正整数为a[i],表示从编号为i的基地到编号为a[i]的基地之间有一条道路,为了更加简洁的描述树状结构的基地群,有a[i]<i。
Output:
输出文件名为output.txt
输出文件仅有一个正整数,表示至少要设立多少个消防局才有能力及时扑灭任何基地发生的火灾。
贪心解法:
首先一个"明显"的贪心是:
对于这棵树,选取最低(深度最大)的没有被覆盖到的点,在他的祖父结点设置消防局
找深度最大的点就很简单可以预处理一下
所以问题就是怎么判断一个点有没有被覆盖到(也是本方法最巧妙的一个地方)
首先要知道的:一个点可以被父亲、祖父、儿子、孙子,兄弟结点覆盖到
令o[i]为离i最近的设置了消防局的结点到i的距离 若o[i]>2即i点没有被覆盖到
怎么维护o数组呢
首先初始化为inf
在给结点的祖父结点设置消防局时,我们发现如果用这个结点去更新子辈和兄弟的o[]会很麻烦
所以我们只要用这个结点更新它的父亲结点和祖父结点
这样的话每个结点都被自己的子辈更新过了
那么怎么被父辈与兄弟更新呢?
我们每次取出一个点时 用它的父辈更新它即可
这样就不用考虑兄弟结点了 因为兄弟结点要么已经更新过它的父辈了要么还没取出来
CODE:
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #define R register 5 #define go(i,a,b) for(R int i=a;i<=b;i++) 6 #define M 1001 7 #define inf 2100000000 8 using namespace std; 9 int rd() 10 { 11 int x=0,y=1;char c=getchar(); 12 while(c<'0'||c>'9'){if(c=='-')y=-1;c=getchar();} 13 while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();} 14 return x*y; 15 } 16 struct node{int x,dep;}a[M]; 17 int n,ans,f[M],d[M]; 18 bool cmp(node x,node y){return x.dep>y.dep;} 19 int main() 20 { 21 freopen("1.in","r",stdin); 22 freopen("1.out","w",stdout); 23 n=rd(); 24 a[1].x=1;f[1]=1;d[1]=inf; 25 go(i,2,n) {f[i]=rd();a[i].dep=a[f[i]].dep+1;a[i].x=i;d[i]=inf;} 26 sort(a+1,a+n+1,cmp); 27 go(i,1,n) 28 { 29 int x=a[i].x,ft=f[x],grf=f[ft]; 30 d[x]=min(d[x],d[ft]+1); 31 d[x]=min(d[x],d[grf]+2); 32 if(d[x]>2) 33 { 34 d[grf]=0;ans++; 35 d[f[grf]]=min(d[f[grf]],1); 36 d[f[f[grf]]]=min(d[f[f[grf]]],2); 37 } 38 } 39 printf("%d",ans); 40 return 0; 41 }