[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;
}
posted @ 2022-04-10 22:40  Coros_Trusds  阅读(28)  评论(0编辑  收藏  举报