285.上司のいないプロム
タイトル説明
会社には1〜Nの番号が付けられたN人の従業員がいます。
それらの関係は、プリンシパルによってルート化されたツリーのようなものであり、親ノードは子ノードの直接のボスです。
各従業員には幸福指数があり、整数Hiで与えられます。ここで、1≤i≤Nです。
現在、記念日の宴会がありますが、直属の上司との会議に積極的に参加するスタッフはいません。
この条件が満たされていることを前提として、主催者は、会議に参加するすべてのスタッフの幸福度の合計が最大になるように、何人かのスタッフを会議に招待し、最大値を見つけることを望んでいます。
入力形式
最初の行の整数N。
次のN行で、i番目の行は従業員iの幸福度指数Hiを表します。
次の行N-1で、各行に整数LとKのペアを入力し、KがLの直接のボスであることを示します。
出力形式
最大の幸福度指数を出力します。
データ範囲
1≤N≤6000、
-128≤Hi≤127
入力サンプル:
7
1
1
1
1
1
1
1
1 3 //つまり、3-> 1
2 3
6 4
7 4
4 5
3 5
サンプル出力:
5
分析
会社全体の従業員がツリーを形成し、各従業員はツリー上のノードです。ノードごとに、0と1の2つの状態があります。選択された状態は1で、選択されていない状態は0です。
状態表現:
f[u][0]
:ノードuをルートとするサブツリー、および
f[u][1]
ポイントuの要件を満たすさまざまなソリューションは選択されません。ノードuをルートとするサブツリー、およびポイントuの要件を満たすさまざまなソリューションが選択されます。
属性:最大、f[u][0]
またはf[u][1]
値は、要件を満たすさまざまなソリューションの最大値です
uノードのサブツリーが要件を満たしているスキームの最大値を見つけます。最終結果をf[u][0]
合計f[u][1]
で選択する必要があり、最大値が選択されます。
- ノードuを選択するかどうかを検討します
- 再帰的に、各子ノードs考えるのI親ノードuとし、条件終了の再帰はリーフノードに到達することです。
親ノードuをルートノード、子ノードsをルートノードと見なします。2つの方法は同じであり、互いに独立しています。
また、親ノードの値は子ノードの値を使用する必要があります。
ルートノードは複数の子ノードS有するようにノードとサブツリーUがあると仮定すると、Iをその子ノードSのそれぞれを考慮すると、私を、それとみなすことができるサブサブツリー子ノードSとIルートとして。
- ノードUが選択されている場合、子ノードはね、私は選択してはいけません。
f [u] [1] = ∑(f [si、0])f [u] [1] = ∑(f [si、0])f [ u ] [ 1 ]=∑ (f [ s i 、0 ] ) - ノードuが選択されていない場合は、サブノードを選択することも選択しないこともできます。その中から最大値を選択するだけです。
f [u] [0] = ∑ max(f [si、0]、f [si、1])f [u] [0] = ∑max(f [si、0]、f [si、1])f [ u ] [ 0 ]=∑m a x (f [ s i 、0 ] 、f [ s i 、1 ] )
注:
データの入力から、ツリーのルートノードが誰であるかがわからないため、自分で処理する必要があります。
ルートノードrootが取得されると、ルートノードのサブツリーと見なされるため、最終的には、ルートルートノードが選択されているかどうかのみが出力され、出力f[root][0]
合計のf[root][1]
大きい方が出力されます。
コード
#include <iostream>
#include <cstring>
#define read(x) scanf("%d",&x)
using namespace std;
const int N=6010;
int hpy[N];
int h[N],e[N],ne[N],idx;//树,N个顶点,N-1条边
//next数组记录后继结点,通过idx记录
int pre[N];//pre数组记录前驱结点,直接通过结点编号记录
int dp[N][2];
void add(int a,int b)//a->b的边
{
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
pre[b]=a;
}
void dfs(int u) //判断以该点为根的子树的最大happy值
{
"初始化,是否选该结点:dp[u][1]和dp[u][0]"
"选中该结点,加上该happ值,不选中时,dp[u][0]=0"
dp[u][1]=hpy[u];
"递归结束的条件,遇到叶子结点,它没有子节点了。只进行初始化,然后就往上返回"
for (int i=h[u];~i;i=ne[i]) {
int j=e[i]; //找它的子节点
dfs(j); //求子节点的最大happy值
//考虑以u为根的子树,是否选子节点?
dp[u][0]+=max(dp[j][0],dp[j][1]); //不选u时,子节点可选可不选,取最大值。
dp[u][1]+=dp[j][0]; //选u时,子节点必不能选。
}
}
int main()
{
memset(h,-1,sizeof h);
int n;
read(n);
for (int i=1;i<=n;i++) read(hpy[i]);
//存树
int l,k; //k是l的直接上司,即k->l的边
for (int i=1;i < n;i++) {
//n个顶点,所以共n-1条边,不能取等号
read(l),read(k);//先输入的是子节点
add(k,l); //存数k->l的边
}
//找树的根节点,记录结点编号,只有根节点没有父结点,pre[rt]==0
int rt=1;
while (pre[rt]) rt++;
dfs(rt);
int res=max(dp[rt][0],dp[rt][1]);
printf("%d",res);
return 0;
}
メインループの最初の行の初期化を忘れないでください。これはしばしば間違いを犯します。
dfs再帰では、再帰の終了条件はリーフノードを満たすことです。子ノードがないため、forループに入りません。初期化後、戻ります。
ツリー構造は、複数のインディグリー、1つのインディグリー、最大で1つのアウトディグリーを持つポイントには存在しません。これにより、各ポイントは1回だけ初期化され、再帰は1回だけ発生します。