[BZOJ4987]Tree

[BZOJ4987]Tree

题目大意

从前有棵树。找出K个点A1,A2,…,Ak。使得∑dis(AiAi+1),(1<=i<=K-1)最小。


很显然选的部分一定是一个联通快 ,然后考虑走这个联通快的过程

因为走联通快有时需要回头,那么一条边可能经过1次 也可能经过两次

这个联通快的直径是最长的一条路径,由于希望总代价最小,我们沿着直径的一端走,遇到分支就走下去再回来,一直到直径的另一头。这样使直径这条链只被走了一次,让最长的代价最小,然后总代价就最小了(好像不太会证 但这个是对的

发现直径上的边只走一次,非直径上的边走两次。


考虑这个东西怎么树形dp

设dp[i][j][k]表示在i的子树中,选j个点,其中k个点为直径的两端,然后做树形背包即可
大致的讲一下转移过程
对于以u为根的树,枚举他的儿子v,考虑在v之前选了j个点,在v中选了t个点,就可以从
dp[u][j]+dp[v][t]转移到dp[u][j+t]
由于要树形背包记得倒着枚举,第三维通过0/1/2来判断u->v这条边要加几次权即可

最终答案是

[min_{i=1}^{n}dp[i][k][2] ]

讲道理为啥不是dp[1][k][2]呢,求大佬评论讲解一下qwq


#include<bits/stdc++.h>
#define rep(i,x,y) for (int i=x;i<=y;i++) 
#define res(i,x,y) for (int i=x;i>=y;i--)
using namespace std;
const int maxn=3010,inf=0x3f3f3f3f;
struct Edge{int v,nex,dis;}edge[maxn<<1];
int head[maxn],cnt=0,n,K;
int dp[maxn][maxn][3],siz[maxn];
inline int read(){
    int x=0;char ch=getchar();
    while (!isdigit(ch)) ch=getchar();
    while (isdigit(ch)) x=x*10+ch-48,ch=getchar();
    return x;
}
inline void addEdge(int u,int v,int dis){
    edge[++cnt]=(Edge){v,head[u],dis};head[u]=cnt;
}
void dfs(int u,int fa){
    dp[u][1][0]=dp[u][1][1]=dp[u][0][0]=0;siz[u]=1;
    for (int i=head[u];i;i=edge[i].nex){
        int v=edge[i].v,w=edge[i].dis;
        if (v==fa) continue;
        dfs(v,u);
        res(j,min(siz[u],K),0)
            res(k,min(siz[v],K),0){
                dp[u][j+k][0]=min(dp[u][j+k][0],dp[u][j][0]+dp[v][k][0]+2*w);
                dp[u][j+k][1]=min(dp[u][j+k][1],dp[u][j][1]+dp[v][k][0]+2*w);
                dp[u][j+k][1]=min(dp[u][j+k][1],dp[u][j][0]+dp[v][k][1]+w);
                dp[u][j+k][2]=min(dp[u][j+k][2],dp[u][j][2]+dp[v][k][0]+2*w);
                dp[u][j+k][2]=min(dp[u][j+k][2],dp[u][j][1]+dp[v][k][1]+w);
                dp[u][j+k][2]=min(dp[u][j+k][2],dp[u][j][0]+dp[v][k][2]+2*w);
            }
        siz[u]+=siz[v];
    }
}
int main(){
    n=read();K=read();
    rep(i,1,n-1){
        int u=read(),v=read(),w=read();
        addEdge(u,v,w);addEdge(v,u,w);
    }
    memset(dp,0x3f,sizeof(dp));
    dfs(1,0);
    int ans=inf;rep(i,1,n) ans=min(ans,dp[i][K][2]);
    printf("%d
",ans);
    getchar();getchar();
    return 0;
}
原文地址:https://www.cnblogs.com/ugly-CYW-lyr-ddd/p/11802078.html