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; }
渺渺时空,茫茫人海,与君相遇,幸甚幸甚