记录编号 268790 评测结果 AAAAA
题目名称 [HZOI 2016] 搜城探宝 最终得分 100
用户昵称 GravatarHzoi_ 是否通过 通过
代码语言 C++ 运行时间 0.000 s
提交时间 2016-06-12 19:16:32 内存使用 0.00 MiB
显示代码纯文本
#include<cstdio>//scanf(),printf()
#include<cstring>//memset()
#include<algorithm>//max()
#include<queue>//队列
using namespace std;
const int maxn=30;
struct edge{
	int to;
	edge* prev;//previous的简写,以from(此处没有显式记录)为起点的上一条边
	edge():to(0),prev(NULL){}
}ee[maxn];
struct node{
	int data,lch,rch,prt;//data:节点的宝藏价值
	node(int data=0,int lch=0,int rch=0,int prt=0):
		data(data),lch(lch),rch(rch),prt(prt){}
	//令空儿子为0并不会引起误会,因为任何节点的子节点都不可能是根节点
}a[maxn];
void insert(int,int);
int dfs(int,int);
void bfs(int);
edge* last[maxn]={NULL};//指向以某个点为起点的最后一条边
int len=0;
int f[maxn][maxn];//f(i,j)表示树i使用j个钥匙(包括开i的门的那个)能获得的最大价值
bool vis[maxn][maxn]={{false}},vist[maxn]={false};
//前一个vis用于记忆化搜索,后一个vist用于广搜建树
int n,m,ans=0;
inline int MAIN(){
	freopen("hzoi_key.in","r",stdin);
	freopen("hzoi_key.out","w",stdout);
	scanf("%d%d",&n,&m);
	if(m>n)m=n;//这是显然的,原因前面已经讲过了
	for(int i=1;i<n;i++){
		int x,y;
		scanf("%d%d",&x,&y);
		insert(x,y);
	}
	bfs(1);//以1为根建树
	a[0].lch=1;
	for(int i=1;i<=n;i++)scanf("%d",&a[i].data);
	m+=2;//这里的原因前面也讲过了
	for(int i=2;i<=n;i++){
		memset(f,0,sizeof(f));
		memset(vis,0,sizeof(vis));
		//这两句话很重要,因为每次计算dp值时树已经发生了变化
		if(i==a[a[i].prt].lch){
			a[a[i].prt].lch=0;
			a[0].rch=i;
			ans=max(ans,dfs(0,m));
			a[a[i].prt].lch=i;
			a[0].rch=0;
		}
		else{
			a[a[i].prt].rch=0;
			a[0].rch=i;
			ans=max(ans,dfs(0,m));
			a[a[i].prt].rch=i;
			a[0].rch=0;
		}
		//以上为分情况处理,其实中间的求dp可以合并,但为了方便还原这里分开写
	}
	printf("%d",ans);
	return 0;
}
inline void insert(int x,int y){//添加一条从x到y的新边
	ee[len].to=y;
	ee[len].prev=last[x];
	last[x]=&ee[len++];
}
inline int dfs(int i,int j){//计算dp值,需要使用记忆化搜索
	if(!j)return 0;
	if(vis[i][j])return f[i][j];//已经算过,直接返回答案
	vis[i][j]=true;//标记为"已经算过"
	if(a[i].lch&&a[i].rch)for(int k=0;k<j;k++)
		f[i][j]=max(f[i][j],dfs(a[i].lch,k)+dfs(a[i].rch,j-k-1));
	else if(a[i].lch)f[i][j]=dfs(a[i].lch,j-1);
	else if(a[i].rch)f[i][j]=dfs(a[i].rch,j-1);
	//这里省略了两个儿子都没有的情况,
	//因为如果没有儿子那么上面的三条if语句都不会满足
	return f[i][j]+=a[i].data;//这里利用了C++语言"赋值语句有返回值"的规定
}
inline void bfs(int st){//使用广度优先搜索建树,虽然数据太弱似乎并不需要搜索建树
	queue<int>q;
	q.push(st);
	while(!q.empty()){
		int x=q.front();
		q.pop();
		vist[x]=true;//标记x为"已完成建树操作"
		for(edge* e=last[x];e;e=e->prev){
			int y=e->to;
			if(vist[y])continue;
			//如果已经完成了y的建树操作则跳过,否则可能会把x的父亲误认为x的儿子
			if(!a[x].lch)a[x].lch=y;
			else a[x].rch=y;
			a[y].prt=x;
			q.push(y);//扩展节点
		}
	}
}
int haha=MAIN();
int main(){;}