该题比较好懂,样例作图如下:
但如何处理呢?没有了思路。难道是全新的算法。看了数据范围,算法的时间复杂度是O(n^2).
树的最大独立集。
以下面稍复杂的例子来研究该题。
对应的输入输出数据:
输入:
7
0 1 1
1 1 2
2 2 3 4
3 1 5
4 1 6
5 0
6 0
输出:
3
树形结构!!!
因为是一棵树,所以对于每个节点,我们都把它当成根节点处理→树形dp!!!
注意,某个士兵在一个结点上时,与该结点相连的所有边将都可以被了望到。
定义状态dp[u][0/1]表示u这个节点不放/放士兵
根据题意,如果当前节点不放置士兵,那么它的子节点必须全部放置士兵(想到难),因为要满足士兵可以看到所有的边,所以
dp[u][0]+=dp[to][1]
其中to是u的子节点
如果当前节点放置士兵,它的子节点选不选已经不重要了(因为树形dp自下而上,上面的节点不需要考虑),所以
dp[u][1]+=min(dp[to][0],dp[to][1]) (极难想到)
数据运行过程
f[5][0]=0,f[5][1]=1
f[3][0]+=f[5][1],即f[3][0]=1
f[3][1]=1+min(f[5][0],f[5][1])=1+0=1
f[2][0]+=f[3][1],即f[2][0]=1
f[6][0]=0,f[6][1]=1
f[4][0]+=f[6][1],即f[4][0]=1
f[4][1]=1+min(f[6][0],f[6][1])=1+0=1
f[2][0]+=f[4][1],即f[2][0]=2
f[2][1]=1+min(f[3][0],f[3][1])+min(f[4][0],f[4][1])=1+1+1=3
f[1][0]+=f[2][1],即f[1][0]=3
f[1][1]=1+min(f[2][0],f[2][1])=1+2=3
f[0][0]+=f[1][0],即f[0][0]=3
f[0][1]=1+min(f[1][0],f[1][1])=1+3=4
min(f[0][0],f[0][1])=3
ybt
通过
测试点 | 结果 | 内存 | 时间 |
测试点1 | 答案正确 | 644KB | 3MS |
测试点2 | 答案正确 | 664KB | 2MS |
测试点3 | 答案正确 | 772KB | 2MS |
测试点4 | 答案正确 | 660KB | 2MS |
测试点5 | 答案正确 | 616KB | 1MS |
LOJ
LUOGU
AC代码:
#include <bits/stdc++.h>
#define maxn 1510
using namespace std;
struct node{
int to,next;
}e[maxn<<1];
int head[maxn],tot,n;
int f[maxn][2];
void add(int u,int v){
tot++,e[tot].to=v,e[tot].next=head[u],head[u]=tot;
}
void init(){
int i,k,u,v,j;
scanf("%d",&n);
for(i=1;i<=n;i++){
scanf("%d%d",&u,&k);
for(j=1;j<=k;j++){
scanf("%d",&v);
add(u,v),add(v,u);//无向图
}
f[u][1]=1;
}
}
void dfs(int u,int fa){
int i,j,k,v;
for(i=head[u];i;i=e[i].next){
v=e[i].to;
if(v==fa)continue;
dfs(v,u);
f[u][0]+=f[v][1];
f[u][1]+=min(f[v][1],f[v][0]);
}
}
int main(){
init();
dfs(0,-1);
printf("%d\n",min(f[0][1],f[0][0]));
return 0;
}
该题收获:
若没接触过,该题的难度远不止普及/提高-。
学习了 最小点覆盖,树的最大独立集的计算,
最大的收获,是模拟数据过程,真实的体验到了数据的流动。