BZOJ5290: [Hnoi2018]道路

【传送门:BZOJ3261


简要题意:

  给出一棵2n-1个点的完全二叉树,非叶子节点为城市,叶子节点为乡村,城市一条边是公路,一条边是铁路,城市i向城市j连边必须满足i>j, 然后乡村有3个参数a[i],b[i],c[i],要求每一个城市选一条通向该城市的路进行翻修(也就是翻修n-1条道路)

  设乡村到首都城市1的路上,未翻修的公路条数是x,未翻修的铁路条数是y,每一个乡村的贡献是:c[i]*(a[i]+x)*(b[i]+y)要求选择一个翻修方案使得所有乡村贡献的总和最小


题解:

  记忆化搜索(DP也可以)

  设f[i][j][k]为当前第i个点,未翻修j条公路,未翻修k条铁路的最小贡献

  因为是二叉树,每次往左右找,分别翻新公路和铁路就可以了


参考代码:

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
using namespace std;
inline int read()
{
    int p=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){p=p*10+ch-'0';ch=getchar();}
    return p*f;
}
typedef long long LL;
struct node
{
    int lc,rc;
}t[21000];
LL f[21000][41][41];
int a[41000],b[41000],c[41000];
int n;
LL dfs(int x,int d1,int d2)
{
    if(x>=n) return LL(c[x])*LL(a[x]+d1)*LL(b[x]+d2);
    if(f[x][d1][d2]!=f[0][0][0]) return f[x][d1][d2];
    f[x][d1][d2]=min(dfs(t[x].lc,d1+1,d2)+dfs(t[x].rc,d1,d2),dfs(t[x].lc,d1,d2)+dfs(t[x].rc,d1,d2+1));
    return f[x][d1][d2];
}
int main()
{
    n=read();
    for(int i=1;i<n;i++)
    {
        int lc=read(),rc=read();
        if(lc<0) lc=abs(lc)+n-1;
        if(rc<0) rc=abs(rc)+n-1;
        t[i].lc=lc;t[i].rc=rc;
    }
    for(int i=n;i<=2*n-1;i++) scanf("%lld%lld%lld",&a[i],&b[i],&c[i]);
    memset(f,63,sizeof(f));
    printf("%lld\n",dfs(1,0,0));
    return 0;
}

 

posted @ 2018-04-23 15:40  Star_Feel  阅读(167)  评论(0编辑  收藏  举报