树形DP+状压+搜索。
刚开始想了一些奇怪的树形DP,都没法做,后来发现是自己对题目理解有误。
如果枚举所有点的决策,那是 O(22n) 。发现对于一个最底层节点,他的贡献只和一条链有关,于是可以考虑从这里下手。
对于一个点i,如果他的所有上司的状态已经确定,那么他的左右子树的决策将互不影响,于是可以用DFS枚举一整条链的状态并进行DP。记f[i][j]表示i结点为根的子树中去j个参加战争的最大答案。那么f[i][j]=max(f[i∗2][a]+f[i∗2+1][b])(其中a+b=j)
粗略估计复杂度(强行忽略n±1):枚举一条链的状态:O(2n);链上n个节点枚举左右子树:O(n∗(2n)2)最底层结点统计答案:O(n);乘起来是O(2n∗n∗(2n)2+2n∗n)是O(2n∗n)级别的
这里有详细估计复杂度(Orz):http://blog.csdn.net/vmurder/article/details/45146301
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 2100
#define C 12
using namespace std;
int n, m, w[N][C], g[N][C], f[N][N];
void dfs(int x, int sta, int d)
{
memset(f[x],0,sizeof(f[x]));
if(d==n)
{
f[x][1]=f[x][0]=0;
for(int j = 1; j < n; j++, sta>>=1)
{
if(sta&1)f[x][1]+=w[x][j];
else f[x][0]+=g[x][j];
}
return;
}
dfs(x<<1,sta<<1,d+1);
dfs(x<<1|1,sta<<1,d+1);
for(int i = 0, ii = 1<<(n-d-1); i <= ii; i++)
for(int j = 0, jj = 1<<(n-d-1); j <= jj; j++)
f[x][i+j]=max(f[x][i+j],f[x<<1][i]+f[x<<1|1][j]);
dfs(x<<1,sta<<1|1,d+1);
dfs(x<<1|1,sta<<1|1,d+1);
for(int i = 0, ii = 1<<(n-d-1); i <= ii; i++)
for(int j = 0, jj = 1<<(n-d-1); j <= jj; j++)
f[x][i+j]=max(f[x][i+j],f[x<<1][i]+f[x<<1|1][j]);
}
int main()
{
scanf("%d%d",&n,&m);
for(int i = 1<<(n-1), ii=(1<<n)-1; i <= ii; i++)
for(int j = 1; j < n; j++)
scanf("%d",&w[i][j]);
for(int i = 1<<(n-1), ii=(1<<n)-1; i <= ii; i++)
for(int j = 1; j < n; j++)
scanf("%d",&g[i][j]);
dfs(1,0,1);
int ans=0;
for(int i = 0; i <= m; i++)
ans=max(ans,f[1][i]);
printf("%d\n",ans);
return 0;
}