昨天本來就能寫的,結果寫了倆個題寫了四五個小時,於是乎拖到今天了。
沒有上司的舞會
這個題就很好寫了,不像狀態壓縮,真的是難理解…大佬博客
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了,準備惡補數學知識三天.