コンパイル(木DP)

コンパイラ
[問題の説明]
私が知られているAndroidのシステムをプレイしたいのでCCFは、オルバンの手紙のメンバーです。
CCFは、偉大なC ++プロジェクトはNソースファイルの合計が含まれて書きました。心の中でCCFは、Nソースファイルは、ツリー構造を構成します。各ソースファイルは、ルートノードは、番号1であるツリー内のノードです。
さて、CCFは、プロジェクトをコンパイルし始めました。たびに、彼は木からチェーン(両方の端点を含む)忠ラインコンパイラを選ぶだろう。コンパイラの特性が、これはチェーンエンドポイントが他のエンドポイントの先祖でなければならない必要です。チェーン点に退化してもよいです。各ソースファイルには、一度だけコンパイルする必要があります。
各ソースファイル(00からFFまでの範囲)システムフラグ値の二進忠番号を有しています。チェーンの各選択肢、このチェーンの上記排他的論理和値アップ固有値の全てのソースファイルのフラグのために。チェーンは、すべての機能選択された値は、コンパイルのコストを取得するために追加されます。
CCFは今== ==すべてのファイルをコンパイルすることを選択した少なくとも数チェーンを知りたいです。==最低価格をコンパイルするときに選択したチェーンの最小数はどのくらい==です。
[入力形式
整数Nの最初の行の
次の行、2進忠N個のシステムは、番号Nの固有値ソースファイルにソース・ファイル番号1を表します。(16進数システム忠は、C / C ++を使用しているの前に2つの、すなわちゼロパディングより少ない小文字を採取「%02Xフォーマット」出力)
以下-二つの整数の(N 1)行、接続されたツリーのエッジの2つの頂点所与。
出力形式] [
行二つの整数。コンパイルされ、選択された最小コストのチェーンの最小数が続きます。忠二つの数は10の出力の形で作られています。
サンプル入力[1]

3
01 02 0F
1 2 1 3

出力例[1]

2 16

説明:最適解は、(1,3)、(2)です。
サンプル入力[2]

5つの
67からD1 33 F0
1 2
1 3
2 4
2 5

出力例[2]

3 288

説明:最適解は、(1,3)、(2,4)、(5)又は(1,3)、(2,5)、(4)です。
[データ]の範囲
2万≤≤N 0。
分析:2.選択、コンパイル済みの最小コストを選択したチェーンの最小数と1つのチェーンの最小:この質問は二つの質問が含まれています。

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
struct node{
    int u;
    int v;
    int next;
};
bool fl[20050] = {0}; //由于建的是无向图,所以遍历时标记父亲节点,避免找儿子时误找到父亲 
node edge[40050]; //记录边 
int tot = -1,first[20050],w[20050],minn[20050],dp[20050][270];
// minn 当前节点为根的子树异或总和的最小值
// dp[u][j] 以点u为根节点,当子树异或值为j时,该子树异或总和的最小值
// minn[u] = min{minn[u],dp[u][j] + j}
// dp[u][j ^ w[u]] = min{minn[Vi]总和 - minn[Vj] + dp[vj][j]} 

void add(int u,int v){//建边 
    edge[++tot].u = u;
    edge[tot].v = v;
    edge[tot].next = first[u];
    first[u] = tot;
}

void back(int x,int sum){//找完叶子结点后回溯时计算dp和minn 
    minn[x] = 0x7f7f7f7f;
    for(int i=0;i<=255;i++){
        for(int j=first[x];j!=-1;j = edge[j].next){
            if(!fl[edge[j].v])dp[x][i ^ w[x]] = min(dp[x][i ^ w[x]],sum - minn[edge[j].v] + dp[edge[j].v][i]);
        }
    }
    for(int i=0;i<=255;i++)
        minn[x] = min(minn[x],dp[x][i] + i);
}

int ans = 0;
void find(int x){//找叶子节点 
    bool leaf = 1;//是否是叶子结点的标记 
    int sum = 0;//所有儿子的minn之和,在计算当前节点的minn和 dp时要用 
    fl[x] = true;
    for(int i=first[x];i!=-1;i = edge[i].next){
        if(fl[edge[i].v]) continue;
        leaf = 0;
        find(edge[i].v);
        sum += minn[edge[i].v];
    }
    fl[x] = false;//将fl还原,以便back函数中判断儿子 
    if(leaf){
        ans++;
        minn[x] = w[x];
        dp[x][w[x]] = 0;
    }
    else back(x,sum);
}


int main(){
    freopen("compiler.in","r",stdin);
    freopen("compiler.out","w",stdout);
    memset(first,-1,sizeof(first));
    memset(dp,0x3f,sizeof(dp));
    int u,v,n;scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%02x",&w[i]);
    for(int i=1;i<n;i++){
        scanf("%d%d",&u,&v);
        //输入不一定是父亲 儿子的顺序,所以建无向图,双向的边 
        add(u,v);
        add(v,u);
    }
    ans = 0;
    find(1);
    printf("%d ",ans);
    printf("%d",minn[1]);
    
    return 0;
}

おすすめ

転載: www.cnblogs.com/Cindy-Chan/p/11294345.html