[JLOI2015]战争调度
这道题是道好题,因为它非常好。如果你没有思路,那么你不妨去想想思路。
题目大意
对于一颗 满二叉树,可以将叶子节点设置为两种状态(简称为 \(0\) 和 \(1\)),如果该叶子节点与其某个祖先的状态相同,那么就有一个贡献值,两者都为 \(0\) 或 \(1\) 时对应两种不同的贡献值,求设置 \(1\) 状态小于 \(m\) 时的最大贡献。
题目分析
想出来一个沙雕 \(dp\),令 \(dp[i,j,0/1]\) 表示以 \(i\) 为根的子树中有 \(j\) 个节点在打仗且 \(i\) 节点是否在打仗的最大总贡献值。
但是推状态推了半个小时无果,然后发现不需要第三维。。。。
令 \(dp[i,j]\) 表示以 \(i\) 为根的子树中有 \(j\) 个节点在打仗的最大总贡献值。
所以状态转移为 \(dp[now,i+j]=\max\{dp[now][i]+dp[now][j]\}\)。即左子树的最优解加上右子树的最优解,\(\verb!dfs!\) 的时候像 \(\verb!dfs!\) 那样推就行了依次向下递归是否选择当前节点。
答案为 \(\max\{dp[1][i]\}(i\in[1,m])\)。
还是套路题,练少了啊。。。
代码
//2022/4/9
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <cstdio>
#include <climits>//need "INT_MAX","INT_MIN"
#include <cstring>//need "memset"
#include <numeric>
#include <algorithm>
#define enter putchar(10)
#define debug(c,que) cerr << #c << " = " << c << que
#define cek(c) puts(c)
#define blow(arr,st,ed,w) for(register int i = (st);i <= (ed); ++ i) cout << arr[i] << w;
#define speed_up() ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define mst(a,k) memset(a,k,sizeof(a))
#define Abs(x) ((x) > 0 ? (x) : -(x))
#define stop return(0)
const int mod = 1e9 + 7;
inline int MOD(int x) {
if(x < 0) x += mod;
return x % mod;
}
namespace Newstd {
char buf[1 << 21],*p1 = buf,*p2 = buf;
inline int getc() {
return p1 == p2 && (p2 = (p1 = buf) + fread(buf,1,1 << 21,stdin),p1 == p2) ? EOF : *p1 ++;
}
inline int read() {
int ret = 0,f = 0;char ch = getc();
while (!isdigit(ch)) {
if(ch == '-') f = 1;
ch = getc();
}
while (isdigit(ch)) {
ret = (ret << 3) + (ret << 1) + ch - 48;
ch = getc();
}
return f ? -ret : ret;
}
inline void write(int x) {
if(x < 0) {
putchar('-');
x = -x;
}
if(x > 9) write(x / 10);
putchar(x % 10 + '0');
}
}
using namespace Newstd;
using namespace std;
const int N = 1025,M = 15;
int fight[N][M],food[N][M],dp[N][N];//dp[i][j]:以 i 为根的子树中有 j 个节点在打仗的最大总贡献值
bool vis[M];
int n,m;
inline void dfs(int now,int dep) {
mst(dp[now],0);
if (dep == 0) {//叶子节点
for (register int i = 1;i <= n; ++ i) {
if (vis[i]) {
dp[now][1] += fight[now][i];
} else {
dp[now][0] += food[now][i];
}
}
return;
}
vis[dep] = false;
dfs(now << 1,dep - 1),dfs(now << 1 | 1,dep - 1);
for (register int i = 0;i <= (1 << (dep - 1)); ++ i) {
for (register int j = 0;j <= (1 << (dep - 1)); ++ j) {
dp[now][i + j] = max(dp[now][i + j],dp[now << 1][i] + dp[now << 1 | 1][j]);
}
}
vis[dep] = true;
dfs(now << 1,dep - 1),dfs(now << 1 | 1,dep - 1);
for (register int i = 0;i <= (1 << (dep - 1)); ++ i) {
for (register int j = 0;j <= (1 << (dep - 1)); ++ j) {
dp[now][i + j] = max(dp[now][i + j],dp[now << 1][i] + dp[now << 1 | 1][j]);
}
}
}
int main(void) {
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
#endif
n = read() - 1,m = read();
for (register int i = 0;i < (1 << n); ++ i) {
for (register int j = 1;j <= n; ++ j) {
fight[i + (1 << n)][j] = read();
}
}
for (register int i = 0;i < (1 << n); ++ i) {
for (register int j = 1;j <= n; ++ j) {
food[i + (1 << n)][j] = read();
}
}
dfs(1,n);
int ans = 0;
for (register int i = 0;i <= m; ++ i) ans = max(ans,dp[1][i]);
printf("%d\n",ans);
return 0;
}