[洛谷P3262]战争调度
題目
傳送門 to luogu
思路
編程要從娃娃抓起,dp\tt{dp}dp 要從葉節點搞起。
畢竟 每個葉節點之間是相互獨立的,并且貢獻只與其祖先(“祖先”不包括自己,下同)有關。
一般樹形 dp\tt{dp}dp 都是存整個子樹的情況。此題相同。f(x)f(x)f(x) 為 xxx 的子樹中所有葉子節點的貢獻和最大值。
假設某個葉節點的所有祖先的狀態都確定,那么枚舉一下這個葉節點的狀態,就可以得到這個點的最優值。
那么倒數第二層,也假設它的所有祖先節點狀態都確定,借用葉子節點的枚舉思想:這個點選擇 000 ,那么其兩個子樹應該分別取最大。而且恰好是前面已經處理了的祖先節點狀態全部確定的最大值!
至于去打仗的平民數量……加入 DP\tt{DP}DP 式考慮范圍即可!
f(s,x,y)=max?u=0yf(s′,x1,u)+f(s′,x2,y?u)f(s,x,y)=\max_{u=0}^{y}f(s',x_1,u)+f(s',x_2,y-u)f(s,x,y)=u=0maxy?f(s′,x1?,u)+f(s′,x2?,y?u)
sss 是祖先節點的狀態(狀壓 一下嘛!),xxx 是當前節點,x1,x2x_{1},x_2x1?,x2? 指兩個兒子。uuu 是去打仗的平民人數。注意兩個 fff 函數中的 s′s's′ 是一樣的哦。
感覺很費……時間復雜度是多少呢?一層一層考慮:(將根節點記為第111層)
第 kkk 層共有 2k?12^{k-1}2k?1 個點,每個點的處理時間為 2k2^{k}2k(s′s's′ 的種類數)×2n?k?1\times 2^{n-k-1}×2n?k?1(左子樹中枚舉打仗的平民個數)×2n?k?1\times2^{n-k-1}×2n?k?1(右子樹中枚舉打仗的平民個數)=22n?k?2=2^{2n-k-2}=22n?k?2,相乘可得:第 kkk 層的總時間復雜度為 22n?22^{2n-2}22n?2 。驚喜!與 kkk 無關!
一共 nnn 層,總時間復雜度 O(n4n)\mathcal O(n4^n)O(n4n) ,常數小。
代碼實現時無需記憶化,因為沒有重復計算。但是父節點需要用子節點的值,所以要存一下,但 不用存 sss 。因為父節點知道 s′s's′ 是啥!
代碼
#include <cstdio> #include <iostream> #include <vector> using namespace std;const int MaxN = 1<<10; int war[MaxN][10], farmer[MaxN][10], n;int dp[MaxN][MaxN], size; void dfs(int s,int x){for(int i=0; i<=size; ++i)dp[x][i] = 0; // 不同的s都要跑,記得initif(size == 1){ // 葉子節點for(int i=0; i<n-1; ++i)if(s>>i&1)dp[x][1] += war[x][i];elsedp[x][0] += farmer[x][i];return ;}size >>= 1;for(int zxy=0; zxy<2; ++zxy){dfs(s<<1|zxy,x<<1);dfs(s<<1|zxy,x<<1|1);for(int i=0; i<=size; ++i)for(int j=0; j<=size; ++j)dp[x][i+j] = max(dp[x][i+j],dp[x<<1][i]+dp[x<<1|1][j]);}size <<= 1; // restore }int main(){scanf("%d",&n); int m; scanf("%d",&m);for(int i=(1<<n>>1); i<(1<<n); ++i)for(int j=0; j<n-1; ++j)scanf("%d",&war[i][j]);for(int i=(1<<n>>1); i<(1<<n); ++i)for(int j=0; j<n-1; ++j)scanf("%d",&farmer[i][j]);size = (1<<n>>1), dfs(0,1);int ans = 0;for(int i=0; i<=m; ++i)ans = max(ans,dp[1][i]);printf("%d",ans);return 0; }總結
以上是生活随笔為你收集整理的[洛谷P3262]战争调度的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 十二星座时间及其英文缩写
- 下一篇: ★为什么不要和“穷人”做朋友?