题目描述
喜欢数学的wlswls最近被萎住了。
现在他一共有1...n1...n这么多数字,取数字ii会得到f[i]f[i]的收益。数字之间有些边,对于所有的i(i != 1)i(i!=1),若ii为奇数,则ii与3i+13i+1之间有边,否则ii与i/2i/2之间有边。如果一条边的两个顶点xyxy都被取了,那么会失去d[min(x, y)]d[min(x,y)]的价值。请问wlswls怎么取,才能使得收益最大?
输入描述
第一行一个整数nn。
接下来一行nn个整数表示ff。
接下来一行nn个整数表示dd。
1 \leq n \leq 1001≤n≤100
1 \leq f[i], d[i] \leq 10001≤f[i],d[i]≤1000
输出描述
输出一个整数表示答案。
样例输入 1
210 10 1 2
样例输出 1
19 思路: 根据题目给的建边条件,建边后会形成一个森林,然后把森林转化为一个0为根节点的树,随后进行树形dp。 定义状态: dp[u][0/1] 0为 第u个节点的子树中不取第u个节点的最多利益值, 1为第u个节点的子树中取第u个节点的最多利益值, 常规的树形dp套路, dp[u][0]+=max(dp[v][0],dp[v][1]); dp[u][1]+=max(dp[v][0],dp[v][1]-d[min(u,v)]);// 一个边上两个节点都取的话,要减去对应的值。 最后max(dp[0][0],dp[0][1])就是我们的答案值。 细节见代码:
#include#include #include #include #include #include #include #include
<> n; repd(i, 1, n) { cin >> f[i]; } repd(i, 1, n) { cin >> d[i]; } repd(i, 2, n) { if (i & 1) { if (3 * i + 1 <= n) { v[i].push_back(3 * i + 1); v[3 * i + 1].push_back(i); mer(i, 3 * i + 1); } } else { v[i].push_back(i / 2); v[i / 2].push_back(i); mer(i, i / 2); } } repd(i, 1, n) { if (pre[i] == 0) { v[0].push_back(i); v[i].push_back(0); } } dfs(0, 0); cout << max(dp[0][0], dp[0][1]) << endl; return 0;}inline void getInt(int* p) { char ch; do { ch = getchar(); } while (ch == ' ' || ch == '\n'); if (ch == '-') { *p = -(getchar() - '0'); while ((ch = getchar()) >= '0' && ch <= '9') { *p = *p * 10 - ch + '0'; } } else { *p = ch - '0'; while ((ch = getchar()) >= '0' && ch <= '9') { *p = *p * 10 + ch - '0'; } }}