BZOJ 4711: 小奇挖矿 树形DP

这个状态的定义非常难想吧...

$f[x][y]$ 表示以 $x$ 为根的子树中,其余点全部移到了应该移动到的位置,也建设了应该建设的仓库,而 $x$ 移动到 $y$ 的最小代价.   

这里是事前钦定 $y$ 不建立仓库的.     

那么,我们考虑如何从 $x$ 的儿子转移到 $x$:   

若 $f[son][b]$ 中 $b$ 与 $y$ 相等,则 $f[x][y]=f[x'][y]+f[son][y]$  
若 $f[son][b]$ 中 $b$ 与 $y$ 不相等,且 $b$ 在 $son$ 的子树中,则直接让 $son$ 去 $b$ 就好了.  

最后一种情况:$b$ 与 $y$ 不相等,且 $b$ 在 $son$ 的子树外,那么你发现这个一定没有 $b$ 与 $y$ 相等的优,按照第二种方式转移就行(反正不会起贡献)    

code:

#include <cstdio> 
#include <string> 
#include <cstring>   
#define N 205       
#define ll long long
using namespace std;  
void setIO(string s) 
{
    freopen((s+".in").c_str(),"r",stdin);  
} 
int n,K,edges; 
int val[N],dis[N][N],hd[N],to[N<<1],nex[N<<1],f[N][N];  
void add(int u,int v) 
{
    nex[++edges]=hd[u],hd[u]=edges,to[edges]=v; 
}
void dfs(int u,int ff) 
{     
    for(int i=hd[u];i;i=nex[i]) if(to[i]!=ff) dfs(to[i],u);    
    for(int i=1;i<=n;++i) 
    {
        f[u][i]=dis[u][i];          
        for(int j=hd[u];j;j=nex[j]) 
        {
            int y=to[j]; 
            int tmp=f[y][i];     
            if(y==ff) continue;     
            for(int k=1;k<=n;++k) tmp=min(tmp,f[y][k]+K);        
            f[u][i]+=tmp;                 
        }   
    }
}
void getdis(int pr,int d,int u,int ff) 
{     
    for(int i=hd[u];i;i=nex[i])   
        if(to[i]!=ff) dis[pr][to[i]]=val[d],getdis(pr,d+1,to[i],u);       
}
int main() 
{ 
    // setIO("input");  
    int i,j;     
    scanf("%d%d",&n,&K);  
    for(i=1;i<n;++i) scanf("%d",&val[i]);  
    for(i=1;i<n;++i) 
    {
        int x,y; 
        scanf("%d%d",&x,&y); 
        add(x,y),add(y,x); 
    }      
    for(i=1;i<=n;++i) getdis(i,1,i,0);             
    dfs(1,0); 
    int cur=1e9; 
    for(i=1;i<=n;++i) cur=min(cur,f[1][i]); 
    printf("%d\n",cur+K);  
    return 0; 
}

  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值