[HAOI2015]樹上染色 樹狀揹包 dp

#4033. [HAOI2015]樹上染色

 

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

2017.9.12新加數據一組 By GXZlegendhtml


 

 

Source

鳴謝bhiaibogf提供

 

SolutionSolution

 

 

#include<ctime>
#include<cstdio>
#include<cstdlib>
#define min(a,b) (a<b?a:b)
#define max(a,b) (a>b?a:b)
const int N=2000+5;
const int M=N<<1;
typedef long long ll;
int tot,to[M],val[M],next[M],head[N],size[N];bool vis[N];
int n,K;ll f[N][N];
inline void add(int x,int y,int z){
    to[++tot]=y;val[tot]=z;next[tot]=head[x];head[x]=tot;
}
void dp(int x){
    int y,w,p,q;
    size[x]=1;vis[x]=1;
    for(int i=head[x];i;i=next[i]){
        if(vis[y=to[i]]) continue;
        dp(y);
        w=val[i];
        p=min(size[x],K);
        q=min(size[y],K);//常數優化 
        for(int j=p;~j;j--){
            for(int k=q;~k;k--){
                ll tv=1LL*(k*(K-k)+(n-size[y]-K+k)*(size[y]-k))*w;
                f[x][j+k]=max(f[x][j+k],f[x][j]+f[y][k]+tv);
            }
        }
        size[x]+=size[y];//常數優化 
    } 
}
int main(){
    srand(time(0));srand(rand());
    scanf("%d%d",&n,&K);K=min(K,n-K);
    for(int i=1,x,y,z;i<n;i++) scanf("%d%d%d",&x,&y,&z),add(x,y,z),add(y,x,z);
    int root=rand()%n+1;//常數優化 
    dp(root);
    printf("%lld",f[root][K]);
    return 0;
}

 

參考:優化

https://acxblog.site/archives/sol-bzoj-4033.htmlspa

https://blog.csdn.net/Diogenes_/article/details/81044483.net

http://www.javashuo.com/article/p-dwfwhsbr-dv.htmlcode

相關文章
相關標籤/搜索