1. 程式人生 > >luogu P4438 [HNOI/AHOI2018]道路 樹形dp

luogu P4438 [HNOI/AHOI2018]道路 樹形dp

傳送門

講一下做題的過程 

Day1 指定標籤搜尋 看題 從入門到入土

噁心 再見吧

Day2 這麼咕了一道題好像不好?還是做一下吧

10 minutes later……

這不就是個二叉樹嗎!

題意就是統計每個點經過的左右邊的路徑上選擇多少條邊進行標記

然後化成一個沒法巧算的式子來噁心你 求最小值

給一個條件就是深度<=40

所以搞一個記搜跑一下所有葉子結點的答案

即dp[x][l][r]表示x的子樹所有點到1的路徑上的最小值和

其中x到根的路徑修理l條公路和r條鐵路

所以記搜搞一搞

從x開始修公路(左兒子)就是dp[x][l][r] = dp[ls[x]][l][r] + dp[rs[x]][l][r+1]

修鐵路(右兒子)就是dp[x][l][r] = dp[ls[x]][l+1][r] + dp[rs[x]][l][r]

然後取個min就行

初值inf所以寫了一個比較gou的異或

Time cost: 15min + 55min

Code:

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #include<cmath>
 5 #include<queue>
 6 #include<vector>
 7 #define
ms(a,b) memset(a,b,sizeof a) 8 #define rep(i,a,n) for(int i = a;i <= n;i++) 9 #define per(i,n,a) for(int i = n;i >= a;i--) 10 #define inf 4e9 11 using namespace std; 12 typedef long long ll; 13 ll read() { 14 ll as = 0,fu = 1; 15 char c = getchar(); 16 while(c < '0' || c > '9
') { 17 if(c == '-') fu = -1; 18 c = getchar(); 19 } 20 while(c >= '0' && c <= '9') { 21 as = as * 10 + c - '0'; 22 c = getchar(); 23 } 24 return as * fu; 25 } 26 const int N = 40005; 27 //head 28 int n; 29 ll a[N],b[N],c[N]; 30 31 int ls[N],rs[N]; 32 int numl[N],numr[N]; 33 ll dp[20005][50][50]; 34 ll dfs(int x,int l,int r) { 35 if(x >= n) return (a[x] + l) * (b[x] + r) * c[x]; 36 if(dp[x][l][r] ^ dp[0][0][0]) return dp[x][l][r]; 37 dp[x][l][r] = dfs(ls[x],l+1,r) + dfs(rs[x],l,r); 38 dp[x][l][r] = min(dp[x][l][r],dfs(ls[x],l,r) + dfs(rs[x],l,r+1)); 39 return dp[x][l][r]; 40 } 41 42 int main() { 43 n = read(); 44 //sons 45 rep(i,1,n-1) { 46 ls[i] = read(),rs[i] = read(); 47 if(ls[i] < 0) ls[i] = -ls[i] + n-1; 48 if(rs[i] < 0) rs[i] = -rs[i] + n-1; 49 } 50 //val of village 51 rep(i,1,n) { 52 int x = i+n-1; 53 a[x] = read(),b[x] = read(),c[x] = read(); 54 } 55 ms(dp,63); 56 printf("%lld\n",dfs(1,0,0)); 57 return 0; 58 }
View Code