D

给定三个矩形的长宽,问他们能否拼成一个正方形

构造

我们可以发现组成的正方形只可能有两种
AA AA
BB BC
CC BC
剩下的都可以旋转或者替换位置得到。
然后枚举判断就行了。

F

树形DP
d[u][cnt][0/1] 表示以u为根节点的子树,有cnt个结点标记为1,根节点标记为0/1时连接0,1的边数
我们通过dfs传递子树的叶子节点数目,依次从每个子树更新根节点的状态,这里用到类似背包的思维
tot 为目前更新到的叶子节点数目,now是当前子树的叶子节点数目

1
2
3
for k = 0 -> now
d[u][j][0] = min(d[u][j][0],min(d[u][j-k][0]+d[v][k][0],d[u][j-k][0]+d[v][k][1]+1))
d[u][j][1] = min(d[u][j][1],min(d[u][j-k][1]+d[v][k][0]+1,d[u][j-k][1]+d[v][k][1]))

然后注意边界,不可达状态标为INF
若u为叶子节点 d[u][0][0] = d[u][1][1] = 0;
若u不为叶子节点 d[u][0][0] = d[u][0][1] = 0;

注意 n = 2 时应该特判

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#include<bits/stdc++.h>
using namespace std;

#define pb push_back
typedef long long LL;

const int INF = 0x3f3f3f3f;
const int maxn = 5010;

vector<int> G[maxn];
int d[maxn][maxn][2];

void check(int &a,int b)
{
if(a>b) a=b;
}

int dfs(int u,int fa)
{
int num = G[u].size(),tot = 0,now = 0;
d[u][0][0] = 0;
d[u][0][1] = 0;
if(num == 1)
{
d[u][0][1] = INF;
d[u][1][1] = 0;
return 1;
}
for(int i=0;i<num;++i)
{
int v = G[u][i];
if(v == fa) continue;
now = dfs(v,u);
tot += now;
for(int j=tot;j>=0;--j)
{
int mn0,mn1;
mn0 = mn1 = INF;
for(int k=0;k<=now;++k)
{
check(mn0,min(d[u][j-k][0]+d[v][k][0],d[u][j-k][0]+d[v][k][1]+1));
check(mn1,min(d[u][j-k][1]+d[v][k][0]+1,d[u][j-k][1]+d[v][k][1]));
}
d[u][j][0] = mn0;
d[u][j][1] = mn1;
}
}
return tot;
}

int main()
{
//freopen("test.txt","r",stdin);
int n;
scanf("%d",&n);
for(int i=0;i<n-1;++i)
{
int u,v;
scanf("%d %d",&u,&v);
G[u].pb(v);
G[v].pb(u);
}
if(n == 2)
{
puts("1");
return 0;
}
int cnt = 0,p = 1;
memset(d,INF,sizeof d);
for(int i=1;i<=n;++i)
{
if(G[i].size() == 1) ++cnt;
else p = i;
}
dfs(p,0);
int ans = min(d[p][cnt/2][0],d[p][cnt/2][1]);
printf("%d\n",ans);
return 0;
}