题意
n(n<=200)个节点的树,树上距离小于等于k(k<=200)的两个点不能同时选,
每个点有个点权ai(1<=ai<=1e5),求最大权重和
题解1
Claris的做法,先bfs一遍,然后倒序扫bfs序列,
每次贪心选这个值,并把距离≤k的点都减去这个点的权值,
代表选这个值给别的点带来的不能选的影响,
如果扫到一个点为正,此时代表把残值加上,相当于反选
倒序是因为,只有所有儿子都确定了,父亲才能被确定,是否应该反选
看似是O(n^2)的
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,k,x,y,z,a[N],head[N],cnt;
bool vis[N];
int q[N],h,t,ans;
struct node{int v,nex;}e[N*2];
void add(int u,int v){e[++cnt]=node{v,head[u]};head[u]=cnt;}
void ext(int u)
{
if(vis[u])return;
vis[u]=1;
q[++t]=u;
}
void dfs(int u,int fa,int d)
{
if(d>k)return;
a[u]-=z;
for(int i=head[u];i;i=e[i].nex)
{
int v=e[i].v;
if(v==fa)continue;
dfs(v,u,d+1);
}
}
int main()
{
scanf("%d%d",&n,&k);
for(int i=1;i<=n;++i)
scanf("%d",&a[i]);
for(int i=1;i<n;++i)
{
scanf("%d%d",&x,&y);
add(x,y),add(y,x);
}
h=1;t=0;
ext(1);
while(h<=t)
{
x=q[h++];
for(int i=head[x];i;i=e[i].nex)
{
int v=e[i].v;
ext(v);
}
}
for(int i=n;i;i--)
{
x=q[i];
if(a[x]<=0)continue;
z=a[x];
ans+=z;
dfs(x,0,0);
}
printf("%d\n",ans);
return 0;
}
题解2
考虑O(n^3)的dp,
dp[i][j]表示对于i来说,只选i以下深度>=j的点的最大代价和,
子树合并的时候,考虑u和v最浅的距离必须>k,u才能选v
判断u选不选的时候,就是dp[u][0]从dp[u][w],w>k处转移过来,
dp数组开得足够大,就不用加判断n和k大小的限制了
#include<bits/stdc++.h>
using namespace std;
#define pb push_back
const int N=205;
int n,u,v,k,a[N],dp[N][N],tmp[N],ans;
//dp[i][j]:i为根 选的点只来自i以下深度>=j的点 的最大代价和
vector<int>e[N];
void dfs(int u,int fa){
for(auto &v:e[u]){
if(v==fa)continue;
dfs(v,u);
for(int i=0;i<N;++i){
tmp[i]=dp[u][i];
}
for(int i=0;i<N;++i){
for(int j=max(0,k-i);j<N;++j){//i+j+1>k u内选v
int w=min(i,j+1);
dp[u][w]=max(dp[u][w],tmp[i]+dp[v][j]);
}
}
for(int i=1;i<N;++i){
dp[u][i]=max(dp[u][i],dp[v][i-1]);
}
}
for(int i=k+1;i<N;++i){//选u
dp[u][0]=max(dp[u][0],dp[u][i]+a[u]);
}
}
int main(){
scanf("%d%d",&n,&k);
for(int i=1;i<=n;++i){
scanf("%d",&a[i]);
}
for(int i=1;i<n;++i){
scanf("%d%d",&u,&v);
e[u].pb(v);e[v].pb(u);
}
dfs(1,-1);
for(int i=0;i<N;++i){
ans=max(ans,dp[1][i]);
}
printf("%d\n",ans);
return 0;
}