编译

[Description]
山山是 2017 级信奥班的成员,因为喜欢玩 Android 系统而出名。
山山写出了一个伟大的 C++工程,一共包含 N 个源文件。在山山的脑海中,N 个源文
件构成一个树形结构。每一个源文件是树上的一个节点,其中 1 号节点是树根。
现在,山山开始编译这个工程。每次他会从树上选择一条链(包含两个端点)进行编译。
由于编译器的特性,要求这条链的一个端点必须是另一个端点的祖先。一条链可以退化成一
个点。每个源文件都需要被编译恰好一次。
每一个源文件都有一个两位十六进制数的标志值(范围从 00 到 ff)。对于每一条选择的
链,把该上面所有源文件的标志值异或起来,得到这条链的特征值。把所有选择的链的特征
值相加,得到这次编译的代价。现在山山想知道至少选择几条链才能编译所有文件。在选择
的链数目最小的时候,编译的代价最小是多少。
[Input]
第一行一个整数 N。
以下一行,N 个两位十六进制数,表示第 1 号源文件到第 N 号源文件的特征值。
(十六进制
数中的字母采取小写,不足两位的在前面补零。亦即 C/C++中使用”%02x”输出的格式。
)
以下(N - 1)行,每行两个整数,给出树上的一条边所连接的两个顶点。
[Output]
一行两个整数。依次为,选择的链的最小数目、编译的最小代价。两个数均以十进制形式输
出。
[Sample]


说明:最优方案为(1, 3), (2, 4), (5)或(1, 3), (2, 5), (4)。
[Tips]
0 ≤ N ≤ 20,000。

因为要求链的一个端点必须是另一个端点的祖先,因此可以论证,链的数量等于
叶子结点的数量;因此需要求的只有最小代价。
定义 dp[u][s] 为考虑子树 u ,并且点 u 所属的链当前的异或和为 s 时的最小代价。
由贪心法,每一个非叶子的点都必与一个子节点相连(否则链的数量无法达到最小),
因此对每一个点枚举与它相连的子节点即可。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 using namespace std;
 6 typedef long long lol;
 7 struct Node
 8 {
 9   int next,to;
10 }edge[40001];
11 int num,head[20001];
12 lol f[20001][256],t[20001],cnt,cost[20001],n;
13 void add(int u,int v)
14 {
15   num++;
16   edge[num].next=head[u];
17   head[u]=num;
18   edge[num].to=v;
19 }
20 void dfs(int x,int pa)
21 {int flag=0,i,j;
22   lol sum=0;
23   for (i=head[x];i;i=edge[i].next)
24     {
25       int v=edge[i].to;
26       if (v!=pa)
27     {flag=1;
28       dfs(v,x);
29       sum+=t[v];
30     }
31     }
32   if (flag==0) 
33     {
34       ++cnt;
35       f[x][cost[x]]=cost[x];
36     }
37   for (i=head[x];i;i=edge[i].next)
38     {
39       int v=edge[i].to;
40       if (v!=pa)
41     {
42       for (j=0;j<=255;j++)
43         {
44           f[x][j^cost[x]]=min(f[x][j^cost[x]],sum-t[v]+f[v][j]+(j^cost[x])-j);
45         }
46     }
47     }
48   for (i=0;i<=255;i++)
49     t[x]=min(t[x],f[x][i]);
50 }
51 int main()
52 {int i,u,v;
53   cin>>n;
54   memset(f,127/2,sizeof(f));
55   memset(t,127/2,sizeof(t));
56   char ch=getchar();
57   for(i=1;i<=n;i++)
58   {
59     while(!(ch>='0'&&ch<='9'||ch>='a'&&ch<='f'))ch=getchar();
60     while(ch>='0'&&ch<='9'||ch>='a'&&ch<='f')
61     {
62       cost[i]=cost[i]*16;
63       if(ch<='9'&&ch>='0')cost[i]+=ch-'0';
64       else cost[i]+=ch-'a'+10;
65       ch=getchar();
66     }
67   }
68   for (i=1;i<=n-1;i++)
69     {
70       scanf("%d%d",&u,&v);
71       add(u,v);add(v,u);
72     }
73   dfs(1,0);
74   cout<<cnt<<' '<<t[1];
75 }
原文地址:https://www.cnblogs.com/Y-E-T-I/p/7699669.html