1. 程式人生 > >【BZOJ 4007】[JLOI2015]戰爭調度 DP+搜索+狀壓

【BZOJ 4007】[JLOI2015]戰爭調度 DP+搜索+狀壓

大小 進行 如果 完全 復雜 戰爭調度 葉子 max 意義

又是一道思路清新的小清晰。

觀察題目,如果我們確定了平民或者貴族的任意一方,我們便可以貪心的求出另一方,至此20分;我們發現層數十分小,那麽我們就也是狀壓層數,用lca轉移,線性dp,至此50分(好像數據很水這麽打能A);至今我們沒有用到他是一棵完全二叉樹,那麽我們發現如果進行樹dp,也就是說從子節點轉移到父節點,f[i][j],以i為根的子樹裏的平民有j個參戰貢獻最大值,我們需要確定平民的請況而且有不能狀壓,但是結合我們上次得出的結論,我們發現如果我們dp狀態的意義為,在確定由此節點到root的所有節點的狀態時,以i為根的子樹裏的平民有j個參戰貢獻最大值,我們就可以不用知道平民的情況了,就是f[i][j][k],那麽我們就可以合並上去了,然而我們發現這樣不僅TLE而且MLE,但是如果我們k那一維通過枚舉而實現呢,我們就可以即時轉移而去掉最後一維,而且丟掉許多無效狀態,然而我們發現k他從最底層到最高層呈現指數遞減,我們可以興奮一下然後認真考慮時間復雜度了:對於每一個出口也就是葉子節點我們最多出去2^10次並且每次算貢獻O(10),於是O(10*2^20),然後每次合並——在根處2^9*2^9*1,往下走一層需要合並的點數乘2^2,合並大小除2,於是總的為層數乘點數平方即O(10*2^20)。於是總時間復雜度O(10*2^20)。

#include <cstdio>
#include <cstring>
#include <algorithm>
#define ls (pos<<1)
#define rs ((pos<<1)|1)
using std::max;
const int N=(1<<10)+50;
int f[N][N],tw[N][20],tg[N][20],w[N][N],g[N][N];
int n,m,len,bin[20];
void read_pre(){
  scanf("%d%d",&n,&m),len=1
<<(n-1); for(int i=1;i<=len;++i) for(int j=1;j<n;++j) scanf("%d",&tw[i][j]); for(int i=1;i<=len;++i) for(int j=1;j<n;++j) scanf("%d",&tg[i][j]); for(int i=1;i<=len;++i) for(int j=0;j<len;++j) for(int k=1;k<n;++k) (j
&(1<<(k-1)))?w[i+len-1][j]+=tw[i][k]:g[i+len-1][j]+=tg[i][k]; bin[n-1]=1; for(int i=n-2;i>0;--i)bin[i]=bin[i+1]<<1; } void dfs(int pos,int deep,int state){ if(deep==n){ f[pos][0]=g[pos][state],f[pos][1]=w[pos][state]; return; } memset(f[pos],0,sizeof(f[pos])); dfs(ls,deep+1,state|bin[deep]), dfs(rs,deep+1,state|bin[deep]); for(int i=0;i<=bin[deep];++i) for(int j=0;j<=bin[deep];++j) f[pos][i+j]=max(f[pos][i+j],f[ls][i]+f[rs][j]); dfs(ls,deep+1,state), dfs(rs,deep+1,state); for(int i=0;i<=bin[deep];++i) for(int j=0;j<=bin[deep];++j) f[pos][i+j]=max(f[pos][i+j],f[ls][i]+f[rs][j]); } void work_print(){ dfs(1,1,0);int ans=0; for(int i=0;i<=m;++i) ans=max(ans,f[1][i]); printf("%d",ans); } int main(){ read_pre(); work_print(); return 0; }

【BZOJ 4007】[JLOI2015]戰爭調度 DP+搜索+狀壓