题解:P6499 [COCI2016-2017#2] Burza
Carey_chen · · 题解
Solution
这是一道不容易独立思考出来的状态压缩动态规划题。
考虑转化题意为:
有一棵树,求能不能在
1 \sim k (根节点为第0 层)上的每一层标记一个点和它的整颗子树,使得第k 层中的每个节点都被标记过。
显然这种可行的情况
因此,当
定义
最后判断覆盖第
Code
#include <bits/stdc++.h>
using namespace std;
const int M = (1 << 20) + 5;
vector<int> G[410];
int cnt = 0, n, k;
int l[410], r[410];
bool dp[410][M];
int p[410];
vector<int> T;
void dfs(int u = 1, int pre = -1, int dep = 0)
{
p[u] = dep;
if(dep == k)
{
cnt++;
l[u] = r[u] = cnt;
T.push_back(u);
return;
}
l[u] = 1e9;
r[u] = -1e9;
for(auto v : G[u])
{
if(v != pre)
{
dfs(v, u, dep + 1);
l[u] = min(l[u], l[v]);
r[u] = max(r[u], r[v]);
}
}
if(u != 1 && l[u] != 1e9 && r[u] != -1e9)
{
T.push_back(u);
}
}
int main()
{
scanf("%d %d", &n, &k);
if(k > sqrt(n))
{
printf("DA\n");
return 0;
}
for(int i = 1; i < n; i++)
{
int u, v;
scanf("%d %d", &u, &v);
G[u].push_back(v);
G[v].push_back(u);
}
dfs(1);
// printf("%d \n", cnt);
// for(auto i : T)
// {
// printf("%d %d %d\n", i, l[i], r[i]);
// }
int N = (1 << k) - 1;
dp[0][0] = true;
for(int i = 0; i <= cnt; i++)
{
for(int S = 0; S <= N; S++)
{
if(dp[i][S] == true)
{
//printf(" %d %d\n", i, S);
for(auto j : T)
{
if(!((S >> (p[j]-1)) & 1) && i + 1 == l[j])
{
//printf(" %d %d\n", l[j], r[j]);
dp[r[j]][S | (1 << (p[j]-1))] |= dp[i][S];
}
}
}
}
}
//printf("%d %d", cnt,N);
bool ans = false;
for(int i = 0; i <= N; i++)
{
ans |= dp[cnt][i];
}
printf(ans ? "DA\n" : "NE\n");
return 0;
}