4033: [HAOI2015]树上染色

4033: [HAOI2015]树上染色

Time Limit: 10 Sec   Memory Limit: 256 MB
Submit: 1462   Solved: 607
[ Submit][ Status][ Discuss]

Description

有一棵点数为N的树,树边有边权。给你一个在0~N之内的正整数K,你要在这棵树中选择K个点,将其染成黑色,并
将其他的N-K个点染成白色。将所有点染色后,你会获得黑点两两之间的距离加上白点两两之间距离的和的收益。
问收益最大值是多少。

Input

第一行两个整数N,K。
接下来N-1行每行三个正整数fr,to,dis,表示该树中存在一条长度为dis的边(fr,to)。
输入保证所有点之间是联通的。
N<=2000,0<=K<=N

Output

输出一个正整数,表示收益的最大值。

Sample Input

5 2
1 2 3
1 5 1
2 3 1
2 4 2

Sample Output

17
【样例解释】
将点1,2染黑就能获得最大收益。

HINT


Source

鸣谢bhiaibogf提供

[ Submit][ Status][ Discuss]

定义状态f[i][j]:以i为根的子树,有j个黑点,最优决策
转移的话考虑每条边的贡献,为子树中黑/白点数 * 子树外黑/白点数 * 边权
这样转移方程显然,按照添加的方式,每次枚举根的大小,枚举子树大小,更新状态
看似O(n^3)??其实并没有,这样的转移可以看作枚举每个点对,因此复杂度是O(n^2)
#include
#include
#include
#include
using namespace std;
 
const int maxn = 2020;
typedef long long LL;
 
struct E{
    int to,w; E(){}
    E(int to,int w): to(to),w(w){}
};
 
LL f[maxn][maxn],g[maxn];
int n,k,siz[maxn];
 
vector  v[maxn];
 
void Dfs(int x,int fa)
{
    siz[x] = 1;
    for (int i = 0; i < v[x].size(); i++)
    {
        E e = v[x][i];
        if (e.to == fa) continue;
        Dfs(e.to,x);
        for (int A = 0; A <= siz[x]; A++)
            for (int B = 0; B <= siz[e.to]; B++)
            {
                if (A + B > k) break;
                LL G = f[x][A] + f[e.to][B];
                G += 1LL * e.w * B * (k - B);
                G += 1LL * e.w * (siz[e.to] - B) * (n - k - siz[e.to] + B);
                g[A + B] = max(g[A + B],G);
            }
        siz[x] += siz[e.to];
        memcpy(f[x],g,sizeof(g)); memset(g,0,sizeof(g));
    }
}
 
int main()
{
    #ifdef DMC
        freopen("DMC.txt","r",stdin);
    #endif
     
    cin >> n >> k;
    for (int i = 1; i < n; i++)
    {
        int x,y,w; scanf("%d%d%d",&x,&y,&w);
        v[x].push_back(E(y,w));
        v[y].push_back(E(x,w));
    }
    Dfs(1,0); cout << f[1][k] << endl;
    /*for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= k; j++)
            printf("%lld ",f[i][j]);
        puts("");
    }*/
    return 0;
}


你可能感兴趣的:(dp,树)