题目描述
要求:
蚂蚁们爬树做游戏。
同一个队伍的蚂蚁站在树上的同一个节点;不同队伍的蚂蚁间隔至少一个空节点。
树上每个节点的容纳蚂蚁数量有限。
目的:
蚂蚁数最多的情况下,蚂蚁数最少的队伍蚂蚁数最多。
程序描述:
给定一棵树,每个节点有一个权值。
选择其中节点,满足被选中的节点两两不相邻。
使得被选择节点权值最大的情况下,最小权值尽可能大。
输入样例:
5 4
3 4 1 4 9
1 2
1 3
2 4
3 5
输出样例:
16 3
样例解释:
当选择1,4,5号节点时,有最大权值之和16,最小权值为1号节点的3,此时为最优解。
输入描述:
第一行数n,m表示树上节点数和边数。
第二行n个数a1,…, an。ai表示第i个节点上可以容纳的最大蚂蚁数。
接下来m行,每行两个数u,v,表示u和v节点可以直接相连。
无重复边无自环,数据为整数。
输出描述:
输出一行,包含两个数,分别表示最大权值和以及最大化的最小值。
分析
一.状态
从dp的角度考虑,先把状态确定好;
而且题目要求输出最大权值和最大化的最小值两个东西,那就用两个数组来保存:
dp[N][2],值表示权值和
mi[N][2],值表示最小值
对于每个节点,有选或者不选两个状态
dp[u][0],选了当前节点的权值和
dp[u][1],不选当前节点的权值和
mi[u][0],选了当前节点的最小值
mi[u][1],不选当前节点的最小值
二.状态转移
dp无非就是把大问题转移成小问题,这道题的状态转移也比较容易想到。
1 选了当前节点,则子节点一定不能选
dp[u][1]+=dp[v][0], 其中v是u的子节点
那么最小值就用 选了当前节点后的最小值 和 不选子节点后的最小值 比较
mi[u][1]=min( mi[u][1], mi[v][0] )
2 不选当前节点,则子节点可选可不选
那子节点到底选不选呢,优先考虑的应该是权值和最大,所以哪种情况大就选哪种情况
dp[u][0]+=max( dp[v][0], dp[v][1])
最大权值和就这么搞定了。
问题来到了最小值上,这个时候就必须分清楚到底子节点有没有选了。
2.1 选了子节点 ( dp[v][1] > dp[v][0] )
既然选了,那就好说,比较两个值就行
mi[u][0]=min( mi[u][0], mi[v][1] )
2.2 没选子节点 ( dp[v][1] < dp[v][0] )
既然没选,那也好说,还是比较两个值
mi[u][0]=min( mi[u][0], mi[v][0] )
2.3 选不选一样 ( dp[v][1] = dp[v][0] )
这就头疼了。
但是题目中第二个条件:最大的最小值,就可以利用到。
我们只需要看到底哪个的最小值比较大,就行了。
mi[u][0]=min( mi[u][0], max( mi[v][0], mi[v][1] ) )
以上,转移过程就解决了,只需要从根部开始dfs,然后就得到了每个点的最大权值和,以及对应的最大的最小值。
代码实现
参考的这个,第4题,链接
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+50;
ll val[N];
vector<int> e[N];
ll dp[N][2];//表示以i为根的子树选了 1/0 当前根/不选根 的最大值
ll mi[N][2];
void dfs(int x,int fa){
dp[x][1]=val[x];
mi[x][1]=val[x];
dp[x][0]=0;
for(auto &son:e[x]){
if(son==fa) continue;
dfs(son,x);
dp[x][1]+=dp[son][0];
mi[x][1]=min(mi[x][1],mi[son][0]);
dp[x][0]+=max(dp[son][1],dp[son][0]);
if(dp[son][1]>dp[son][0]) mi[x][0]=min(mi[x][0],mi[son][1]);
else if(dp[son][1]<dp[son][0]) mi[x][0]=min(mi[x][0],mi[son][0]);
else mi[x][0]=min(mi[x][0],max(mi[son][0],mi[son][1]));
}
}
int main(){
memset(mi,63,sizeof mi);
int n,m;cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>val[i];
}
for(int i=1;i<n;i++){
int x,y;cin>>x>>y;
e[x].push_back(y);
e[y].push_back(x);
}
dfs(1,0);
ll S1=0,S2=0;
for(int i=1;i<=n;i++){
S1=max(S1,max(dp[i][0],dp[i][1]));
}
cout<<S1<<" ";
for(int i=1;i<=n;i++){
if(S1==dp[i][0]) S2=max(S2,mi[i][0]);
if(S1==dp[i][1]) S2=max(S2,mi[i][1]);
}
cout<<S2;
return 0;
}