Codeforces Round #631 (Div. 2) - Thanks, Denis aramis Shitov! 比赛人数10889
[codeforces 1330E] Drazil Likes Heap 堆元素删除+堆深度+贪心
总目录详见https://blog.csdn.net/mrcrack/article/details/103564004
在线测评地址https://codeforces.com/contest/1330/problem/E
Problem | Lang | Verdict | Time | Memory |
---|---|---|---|---|
E - Drazil Likes Heap | GNU C++11 | Accepted | 545 ms | 12200 KB |
题意理解:
以如下数据为例
3 2
7 6 3 5 4 2 1
10
3 2 3 1
说明:
3 2 3 1分别对应,最新堆的元素位置
上述数据生成过程,如下图
上述数据为什么不能依次删去最新堆中的最大值:7,6,5,4,若按这种方式删除,对应的数据生成过程,如下图
很明显,这种删除方式,高度未变,故错误。
思路同https://www.cnblogs.com/huangdalaofighting/p/12637116.html
1.每次删除一个节点,由它大儿子(数值大的儿子)和大孙子(数值大的孙子)组成的链的深度−1。
2.一个节点不可删,当且仅当它大儿子(数值大的儿子)和大孙子(数值大的孙子)等组成的链的深度等于g。
3.一个节点不可删,那么它的大儿子(数值大的儿子)也不可删。由它大儿子(数值大的儿子)和大孙子(数值大的孙子)组成的链都不能删。
于是得到贪心算法,如果根节点能删则删,否则把左右儿子当作根分别进行删除操作。
这样能够保证随后得到的堆的元素和是最小的。
上述数据,贪心算法如下
AC代码如下。
#include <stdio.h>
#define maxn 2100000
#define LL long long
#define lson(x) (x<<1)
#define rson(x) ((x<<1)|1)
int a[maxn],b[maxn/2],g,h,cnt,n,m;
LL sum;
void del(int x){//删除x位置处元素
if(a[lson(x)]==0&&a[rson(x)]==0)a[x]=0;
else{
if(a[lson(x)]>a[rson(x)])a[x]=a[lson(x)],del(lson(x));
else a[x]=a[rson(x)],del(rson(x));
}
}
int getdepth(int x,int dep){//求x这条链上对应的深度.dep表示当前x位置所在深度
if(a[lson(x)]>a[rson(x)])return getdepth(lson(x),dep+1);
else if(a[lson(x)]<a[rson(x)])return getdepth(rson(x),dep+1);//a[lson(x)]<a[rson(x)]
else return dep;//a[lson(x)]==a[rson(x)]==0;
}
void dfs(int x,int dep){//dep表示当前x位置所在深度
if(a[x]==0)return;
while(getdepth(x,dep)>g)sum-=a[x],b[++cnt]=x,del(x);
dfs(lson(x),dep+1);//左子树
dfs(rson(x),dep+1);//右子树
}
int main(){
int i,t;
scanf("%d",&t);
while(t--){
scanf("%d%d",&h,&g);
n=(1<<h)-1,m=(1<<g)-1;
for(i=1;i<=m;i++)b[i]=0;//初始化别忘了
for(i=n+1;i<=2*n+1;i++)a[i]=0;//最后一个元素位置n,左孩子2*n,右孩子2*n+1
sum=0,cnt=0;//初始化别忘了
for(i=1;i<=n;i++)scanf("%d",&a[i]),sum+=a[i];
dfs(1,1);
printf("%lld\n",sum);
for(i=1;i<n-m;i++)printf("%d ",b[i]);
printf("%d\n",b[n-m]);
}
return 0;
}
编写过程,收获颇丰,全是自己编写调试出来的。
弄明白了(1<<3)=8,(3<<1)=6.
树上递归的编写有些感觉了。
明白了,在多重测试数据下,变量的初始化太重要了。