码迷,mamicode.com
首页 > Windows程序 > 详细

P3621 【[APIO2007]风铃】

时间:2019-06-06 18:45:56      阅读:110      评论:0      收藏:0      [点我收藏+]

标签:pen   line   转移   int   optimize   左右   OLE   ==   def   

这个故事告诉我们,万物皆可暴(du)力(liu)dp。
首先,题目的大意就是让我们通过他给定的一些变换方式使得这棵树变为完全二叉树。我们把完全二叉树所有的叶子节点连起来,应该只有两种情况,一种是一条链,另一种是两条链,其中一条链上的点的深度比另一条链的深度大1。
令dp(i,j)表示i节点状态为j时的最小移动次数。
状态无非三种:
1.一条较深的链。
2.一条较浅的链。
3.两条链。
我们注意到,对于一个点,不管怎么移动,深度总是不变,所以对于状态1和状态2,不需要向下递归,只需要查看这棵子树内所有的叶子节点的深度是否为对应值就可以了。

第三种情况的转移就是下面六种情况:
1.1+2
2.1+3
3.3+2
4.2+1(+1)
5.3+1(+1)
6.2+3(+1)加一指的是左右子树交换的花费

代码在下面:

#pragma GCC optimize("Ofast,fast-math")
#pragma GCC target("avx,avx2")
#pragma GCC optimize("O2")
#include<bits/stdc++.h>
#define gc pa==pb&&(pb=(pa=buf)+fread(buf,1,1<<17,stdin),pa==pb)?EOF:*pa++
static char buf[1<<17],*pa(buf),*pb(buf);
inline int read() {
	register int s=0,f=1;
	register char ch(gc);
	while(ch<‘0‘||ch>‘9‘) ch==‘-‘?f=-1,ch=gc:ch=gc;
	while(ch>=‘0‘&&ch<=‘9‘) s=s*10+ch-48,ch=gc;
	return s*f;
}
using namespace std;
struct yuansu {
	int x,y;
} bian[200005];
int minn(10000000);
int b[100005],dui[100005],deepth[100005];
int lef[100005],rig[100005];
int f(1);
int n,x,y;
int possible[100005][2];
int cmp(yuansu a,yuansu b) {
	return a.x<b.x;
}
int min(int a,int b) {
	return a<b?a:b;
}
int bfs() {
	int head(1),tail(2);
	b[0]=b[1]=1;
	dui[1]=1;
	while(head!=tail) {
		int i(dui[head]);
		if(!lef[i]) {
			if(minn!=10000000) {
				if((minn>deepth[dui[head]]+1)||(minn<deepth[dui[head]]-1))return 1;
			}
			if(minn!=deepth[dui[head]]&&minn!=10000000)f=0;
			minn=min(minn,deepth[dui[head]]);
		}
		if(!rig[i]) {
			if(minn!=10000000) {
				if((minn>deepth[dui[head]]+1)||(minn<deepth[dui[head]]-1))return 1;
			}
			if(minn!=deepth[dui[head]]&&minn!=10000000)f=0;
			minn=min(minn,deepth[dui[head]]);
		}
		if(!b[lef[i]]) {
			dui[tail]=lef[i];
			deepth[dui[tail]]=deepth[dui[head]]+1;
			tail++;
		}
		if(!b[rig[i]]) {
			dui[tail]=rig[i];
			deepth[dui[tail]]=deepth[dui[head]]+1;
			tail++;
		}
		head++;
	}
	return 0;
}
void dfs(int dian) {
	if(!lef[dian]) {
		if(deepth[dian]==minn)
			possible[dian][0]=1;//若该点为浅点,则包含该点的子树不可能出现深状态(也就是0状态)
		else
			possible[dian][1]=1;
	}
	if(!rig[dian]) {
		if(deepth[dian]==minn)
			possible[dian][0]=1;
		else
			possible[dian][1]=1;
	}
	if(lef[dian]) {
		dfs(lef[dian]);
		possible[dian][0]|=possible[lef[dian]][0];
		possible[dian][1]|=possible[lef[dian]][1];
	}
	if(rig[dian]) {
		dfs(rig[dian]);
		possible[dian][0]|=possible[rig[dian]][0];
		possible[dian][1]|=possible[rig[dian]][1];
	}
	return;
}
void chushihua() {
	dfs(1);
	return;
}
int violence_dp(int dian,int zt) {
	if(zt==0||zt==1) {
		if(!possible[dian][zt])return 0;
		else return 100000000;
	} else {
		int ans(100000000);
		if(lef[dian]==0&&rig[dian]==0)return 100000000;
		else if(!lef[dian]&&rig[dian]) {
			if(deepth[dian]==minn) {
				ans=min(ans,violence_dp(rig[dian],0)+1);//1+0 (+1)
				ans=min(ans,violence_dp(rig[dian],2)+1);//1+2 (+1)
			} else {
				ans=min(ans,violence_dp(rig[dian],1));//0+1
				ans=min(ans,violence_dp(rig[dian],2));//0+2
			}
		} else if(lef[dian]&&!rig[dian]) {
			if(deepth[dian]==minn) {
				ans=min(ans,violence_dp(lef[dian],0));//0+1
				ans=min(ans,violence_dp(lef[dian],2));//2+1
			} else {
				ans=min(ans,violence_dp(lef[dian],1)+1);//1+0 (+1)
				ans=min(ans,violence_dp(lef[dian],2)+1);//2+0 (+1)
			}
		} else {
			ans=min(ans,violence_dp(lef[dian],0)+violence_dp(rig[dian],1));//0+1
			ans=min(ans,violence_dp(lef[dian],2)+violence_dp(rig[dian],1));//2+1
			ans=min(ans,violence_dp(lef[dian],0)+violence_dp(rig[dian],2));//0+2
			ans=min(ans,violence_dp(lef[dian],1)+violence_dp(rig[dian],0)+1);//1+0 (+1)
			ans=min(ans,violence_dp(lef[dian],1)+violence_dp(rig[dian],2)+1);//1+2 (+1)
			ans=min(ans,violence_dp(lef[dian],2)+violence_dp(rig[dian],0)+1);//2+0 (+1)
		}
		return ans;
	}
}
int main() {
	freopen("mobiles.in","r",stdin);
	freopen("mobiles.out","w",stdout);
	n=read();
	for(int i=1; i<=n; i++) {
		x=read();
		y=read();
		if(x>0) {
			lef[i]=x;
		}
		if(y>0) {
			rig[i]=y;
		}
	}
	if(bfs()) {
		cout<<-1;
		return 0;
	}
	chushihua();
	if(f) {//判断是不是一颗满二叉树
		cout<<0;
		return 0;
	}
	if(violence_dp(1,2)>10000000)cout<<-1;
	else cout<<violence_dp(1,2);
	return 0;
}

 


    }
    chushihua();
    if(f) {//判断是不是一颗满二叉树
        cout<<0;
        return 0;
    }
    if(violence_dp(1,2)>10000000)cout<<-1;
    else cout<<violence_dp(1,2);
    return 0;
}

P3621 【[APIO2007]风铃】

标签:pen   line   转移   int   optimize   左右   OLE   ==   def   

原文地址:https://www.cnblogs.com/thedreammaker/p/10986448.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!