NKOJ 3446 (HN Training 2015)Shopping (點分治+樹形dp)

P3446【HN Training 2015 Round7】

問題描述
這裏寫圖片描述

這裏寫圖片描述
這裏寫圖片描述


容易發現最後答案是樹上的一個聯通塊,但直接dp難度較大,考慮用點分治轉化成一定包含根的聯通塊。

點分治後,每一層考慮包含根的聯通塊,那麼轉化成一個樹形依賴揹包,只有選了父節點才能選子節點,並且每個物品有個數限制。

這裏對於這種樹形依賴dp,可以採用dfs序來簡化,因爲在dfs序中,一顆子樹必然是連續的一段,那麼令F[i][j] 表示在dfsinj ,因此在物品數量均爲1時可以得到dp方程

F[i][j]=max(F[i+size[i]][j],F[i+1][jc[i]]+w[i])

第一個轉移表示不選i節點的物品,那麼跳過i這顆子樹,第二個轉移表示選。答案就是F[1][m]

接着考慮物品數量的限制,這裏我用的二進制拆分的方法,即將d個物品拆成log d 個物品,舉個例子,比如將10 個物品可以拆成1,2,4,3 這4個物品,容易發現,無論從這10個物品中取多少個,都可以用上述4個物品表示出來。

修改後的dp方程可以參照代碼,改動不大,只是多了一個log 的複雜度。最終複雜度Tnmlognlogd ,實際上跑得還是很快。


代碼:

#include<stdio.h>
#include<algorithm>
#include<cstring>
#define N 505
#define M 4005
using namespace std;
int T,n,m,w[N],c[N],d[N],F[N][M],ans;
int TOT,LA[N],NE[N*2],EN[N*2];
int Min,rt,si[N],VT,sz[N],wi[N],ci[N],di[N];
bool mark[N];
void ADD(int x,int y)
{
    TOT++;
    EN[TOT]=y;
    NE[TOT]=LA[x];
    LA[x]=TOT;
}
void Gsi(int x,int f)
{
    int i,y;si[x]=1;
    for(i=LA[x];i;i=NE[i])
    {
        y=EN[i];
        if(y==f||mark[y])continue;
        Gsi(y,x);si[x]+=si[y];
    ans=max(ans,F[1][m]);
    }
}
void Grt(int x,int f,int s)
{
    int i,y,Max=s-si[x];
    for(i=LA[x];i;i=NE[i])
    {
        y=EN[i];
        if(y==f||mark[y])continue;
        Grt(y,x,s);
        if(Max<si[y])Max=si[y];
    }
    if(Max<Min)Min=Max,rt=x;
}
void DFS(int x,int f)
{
    int i,y,p=++VT;si[x]=1;
    for(i=LA[x];i;i=NE[i])
    {
        y=EN[i];
        if(mark[y]||y==f)continue;
        DFS(y,x);si[x]+=si[y];
    }
    sz[p]=si[x];wi[p]=w[x];ci[p]=c[x];di[p]=d[x];
}
void Gans(int x)
{
    int i,j,k,y;VT=0;DFS(x,0);//DFS序
    for(i=VT;i>0;i--)
    {
        for(j=0;j<=m;j++)F[i][j]=F[i+sz[i]][j];
        for(k=1,y=di[i];y>0;y-=k,k<<=1)//拆分物品
        for(j=m;j>=k*ci[i]||j>=y*ci[i];j--)//揹包
        if(k<y)F[i][j]=max(F[i][j],max(F[i][j-k*ci[i]],F[i+1][j-k*ci[i]])+k*wi[i]);
        else F[i][j]=max(F[i][j],max(F[i][j-y*ci[i]],F[i+1][j-y*ci[i]])+y*wi[i]);
    }
    ans=max(ans,F[1][m]);
    for(i=VT;i>0;i--)
    for(j=0;j<=m;j++)F[i][j]=0;
}
void DC(int x)
{
    int i,y;mark[x]=1;
    Gans(x);
    for(i=LA[x];i;i=NE[i])
    {
        y=EN[i];if(mark[y])continue;
        Min=1e9;Gsi(y,0);Grt(y,x,si[y]);DC(rt);
    }
}
int main()
{
    int i,j,k,x,y;
    scanf("%d",&T);
    while(T--)
    {
        TOT=0;ans=0;
        memset(LA,0,sizeof(LA));
        memset(mark,0,sizeof(mark));
        scanf("%d%d",&n,&m);
        for(i=1;i<=n;i++)scanf("%d",&w[i]);
        for(i=1;i<=n;i++)scanf("%d",&c[i]);
        for(i=1;i<=n;i++)scanf("%d",&d[i]);
        for(i=1;i<n;i++)
        {
            scanf("%d%d",&x,&y);
            ADD(x,y);ADD(y,x);
        }
        Min=1e9;Gsi(1,0);Grt(1,0,n);DC(rt);
        printf("%d\n",ans);
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章