1. 程式人生 > 其它 >P3262 [JLOI2015]戰爭排程 - 樹形 dp,狀壓 dp

P3262 [JLOI2015]戰爭排程 - 樹形 dp,狀壓 dp

題解

由於這是一棵滿二叉樹,所以其樹高只有 \(n\)

\(f_{i,j,S}\)\(i\) 的子樹內,有 \(j\) 個平民參戰,且 \(\operatorname{fa}(i)\sim 1\) 的路徑上的點,顏色為 \(S\) 的對應二進位制位。轉移就是普通的樹上揹包。

乍一看時間複雜度不太對,但 \(f\) 只有 \(\sum_{i=1}^n 2^i\times 2^{n-i}\times 2^i\) 個狀態;一個狀態 \((i,j,S)\) 的轉移複雜度是 \(\mathcal{O}(j)\)。於是總複雜度是 \(\mathcal{O}(n4^n)\)

但這樣 \(f\)

比較難儲存,發現可以把 \(S\) 通過 dfs 砍掉。

Bonus:應該可以通過上下界優化,讓時間複雜度變成 \(\mathcal{O}(4^n)\)

程式碼

#include <cstdio>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <vector>
using namespace std;
#define For(Ti,Ta,Tb) for(int Ti=(Ta);Ti<=(Tb);++Ti)
#define Dec(Ti,Ta,Tb) for(int Ti=(Ta);Ti>=(Tb);--Ti)
template<typename T>
void Read(T &_x){
	_x=0;int _f=1;
	char ch=getchar();
	while(!isdigit(ch)) _f=(ch=='-'?-1:_f),ch=getchar();
	while(isdigit(ch)) _x=_x*10+(ch^48),ch=getchar();
	_x*=_f;
}
template<typename T,typename... Args>
void Read(T &_x,Args& ...others){
	Read(_x);Read(others...);
}
typedef long long ll;
const int Vertex=1030;
int n,m,son[Vertex][2],fa[Vertex],dep[Vertex],cont[Vertex][Vertex][2];
ll f[Vertex][Vertex];
void Dfs(int u,int stat){
	memset(f[u],0,sizeof(f[u]));
	if(!son[u][0]){
		for(int v=fa[u],st=stat;v;v=fa[v],st>>=1){
			if(st&1) f[u][1]+=cont[u][v][1];
			else f[u][0]+=cont[u][v][0];
		}
		return;
	}
	For(i,0,1){
		Dfs(son[u][0],stat<<1|i);
		Dfs(son[u][1],stat<<1|i);
		int mx=(1<<(n-dep[u]));
		For(j,0,min(mx,m)){
			For(k,0,j){
				f[u][j]=max(f[u][j],f[son[u][0]][k]+f[son[u][1]][j-k]);
			}
		}
	}
}
int main(){
	Read(n,m);
	For(i,1,(1<<(n-1))-1) son[i][0]=i<<1,son[i][1]=i<<1|1;
	For(i,1,(1<<n)-1) fa[i]=i/2,dep[i]=dep[fa[i]]+1;
	For(i,1,1<<(n-1)){
		int u=(1<<(n-1))+i-1,v=fa[u];
		For(j,1,n-1){
			int x;Read(x);
			cont[u][v][1]=x;
			v=fa[v];
		}
	}
	For(i,1,1<<(n-1)){
		int u=(1<<(n-1))+i-1,v=fa[u];
		For(j,1,n-1){
			int x;Read(x);
			cont[u][v][0]=x;
			v=fa[v];
		}
	}
	Dfs(1,0);
	ll ans=0;
	For(i,0,m) ans=max(ans,f[1][i]);
	printf("%lld\n",ans);
	return 0;
}
Written by Alan_Zhao