题目
给定一棵带点权树,选出一个最佳的根节点,使得根节点的点权不变,它的儿子点权加(1),其余点点权加(2),并使最大点权最小,输出这个最小的最大点权。(我爱死洛谷上的这个翻译了!原本老长的题设精简成了这么简单易懂的样子)
解说
一眼看去是不是树形(DP)?太复杂了不想写啊,能写别的是不可能写树形(DP)的,这辈子都不可能写树形(DP)的。这么简单的题意,肯定有别的写法吧?
确实有!
总感觉这个题没有代码不太好说,直接看下面代码的注释吧……
代码
#include<bits/stdc++.h>
using namespace std;
#define v e[j].to//注意这里,下面的v都是e[j].to
const int maxn=300000+3;
int a[maxn];
struct edge{
int next,to;
}e[2*maxn];
int n,ans,fin,tot,head[maxn];//fin代表最终答案
map<int,int> m;//键为点权,值为编号
//因为权值有负数不能用数组
void Add(int a,int b){
e[tot].to=b;
e[tot].next=head[a];
head[a]=tot;
tot++;
}
int main(){
ans=-1000000000;//找最大值
tot=1;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
ans=max(ans,a[i]);
m[a[i]]++;//记录权值出现了几次
}
for(int i=1;i<=n-1;i++){
int x,y;
scanf("%d%d",&x,&y);
Add(x,y);
Add(y,x);
}
fin=ans+2;//如果没有更小答案fin就是ans+2
for(int i=1;i<=n;i++){
bool judge=0;
for(int j=head[i];j;j=e[j].next){
m[a[v]]--;//记录儿子的权值
if(a[v]==ans) judge=1;//记录儿子里是否有最大值
}
if(!m[ans]) fin=ans+1;//这说明所有的最大值都在儿子里,所以fin=ans+1
if(m[ans]==1&&a[i]==ans){//这说明最大值在根节点
if(judge) fin=ans+1;//最大值在根节点的情况下儿子里还有最大值,肯定儿子因为加了1较大
else if(m[ans-1]) fin=ans-1+2;//儿子里面没有最大值了但是儿子之外有权值为ans-1的节点权值+2
//所以这里为了看的明白没有直接写ans+1
else{
fin=ans;//只有根节点是最大值了,没有别的可以超过
break;//记得break!!!在这里栽了两次
//为什么要break?这已经是最佳答案了,之后再计算会再变回更大的值
}
}
for(int j=head[i];j;j=e[j].next) m[a[v]]++;
}
printf("%d",fin);
}
幸甚至哉,歌以咏志。