昨天本来就能写的,结果写了俩个题写了四五个小时,于是乎拖到今天了。
没有上司的舞会
这个题就很好写了,不像状态压缩,真的是难理解…大佬博客
f[u][0]表示从以U为根节点,不选u的方案。
f[u][1]表示从以U为根节点,选u的方案。
所以f[u][0] = ∑max(f[si][0],f[si][1])i是遍历下一层的子节点
f[u][0] = ∑f[si][0] i是遍历下一层的子节点
#include<bits/stdc++.h>
using namespace std;
const int N=6010;
int happy[N];
int f[N][2];
int h[N],e[N],ne[N],idx;
bool st[N];
int n;
vector<int> ve[N];
void dfs(int u)
{
f[u][0]=0;
f[u][1]=happy[u];
for(int i=0;i<ve[u].size();i++)
{
int j=ve[u][i];
dfs(j);
f[u][0]+=max(f[j][0],f[j][1]);
f[u][1]+=f[j][0];
}
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
cin>>happy[i];
for(int i=0;i<n-1;i++)
{
int a,b;
cin>>a>>b;
ve[b].push_back(a);
st[a]=true;
}
int root=1;
while(st[root])
root++; //求取根节点
dfs(root);
cout<<max(f[root][0],f[root][1])<<endl;
return 0;
}
其实更下面喜欢这样写…感觉舒服多了…
如果是线型的一串一维数组的话 不能选择相邻的数字 要让选择的数的和最大
那么就有递推方程dp[i] = max(dp[i-1],dp[i-2] + a[i])
max里面前一个式子代表我不选a[i] 那么就能从dp[i-1] 转移过来
后面一个代表我选了这个 那么就是从dp[i-2] + a[i] 转移过来
两者取最大值就是我们需要的答案 那么这题的话就可以用这个思想
如果我们选择了这个节点的值的话 我们就只能从这个节点出发的下下一层转移了 下一层不能选了
如果我们不选这个节点 我们直接从下一层节点转移就好了 两者取最大值 附:大佬博客
#include<bits/stdc++.h>
using namespace std;
const int maxn = 6010;
int val[maxn] , in[maxn];
int dp[maxn] = {0};// 记忆化优化 相当于剪枝了
vector<int> g[maxn]; // 记录树
int dfs(int root)
{
int tans = dp[root]; // 如果前面搜索到了就直接输出就好了
if(tans!=0) return tans;
if(g[root].size() == 0) return val[root]; // 递归到叶子节点直接返回
int ans = val[root] , ans1 = 0 ;// ans 代表我选择了这个节点 ans1 代表我不选择这个节点
for(int i = 0 ; i < g[root].size(); i++) // 第一层
{
for(int j = 0 ; j < g[g[root][i]].size(); j++)
{
ans += dfs(g[g[root][i]][j]);// 那么按照刚才的思路 ans就是从下下一层转移
}
ans1 += dfs(g[root][i]);// 我不选这个就可以直接从这一层转移
tans = max(ans1,ans); // 两者取一下最大值
}
return dp[root] = tans; // 记忆化 避免很多的多余运算
}
int main()
{
int n;
cin>>n;
for(int i = 1; i <= n; i++)
{
cin>>val[i];
}
int u,v;
while(cin>>u>>v&&u!=0)
{
g[v].push_back(u);
in[u]++;
}
int root , ans = 0;
for(int i = 1; i <= n; i++)
{
if(in[i]==0) root = i; // 找到根节点开始dfs
}
ans = dfs(root);
cout<<ans<<endl;
}
怎么说呢,这几天写DP写的真的是心累,智商低的人的苦你们是不会理解的…未来三天写不写DP了,准备恶补数学知识三天.