类1:最小边覆盖问题
题意
在一棵树上选择最少的点使得不被选择的点每个点至少连一条边
思路
状态dp[u][]表示u这个节点不放或放士兵
i:如果当前节点不放置士兵,那么它的子节点必须全部放置士兵
dp[u][0]+=dp[v][1]
ii:如果当前节点放置士兵,它的子节点选不选都可
dp[u][1]+=min(dp[v][0],dp[v][1])
代码
void dfs(int u) {
vis[u]=1;
for(int i=0;i<ve[u].size();i++){
int v=ve[u][i];
if(!vis[v]){
dfs(v);
dp[u][0]+=dp[v][1];
dp[u][1]+=min(dp[v][0],dp[v][1]);
}
}
}
类2:最小点覆盖问题
题意
Farmer John 想让他的所有牛用上手机以便相互交流。他需要建立几座信号塔在 N 块草地中。已知与信号塔相邻的草地能收到信号。给你N−1 个草地(A,B) 的相邻关系,问:最少需要建多少个信号塔能实现所有草地都有信号。
思路
如果选自己,那么是子节点min+1
dp[i][0]+=min(dp[v][0],dp[v][1],dp[v][2])被自己覆盖(预设为1)
如果选儿子,那么有任意一个儿子即可,值为最小子节点(选)+其他(不选)
dp[i][1]=tmp+sum(f[v][0],f[v][1])被自己儿子覆盖
如果选父亲,那么是子节点
dp[i][2]+=min(dp[v][0],dp[v][1])被父亲覆盖
代码
void dfs(int u) {
vis[u]=1;
dp[u][0]=1;
int tmp=0x3f3f3f3f,fl=0;
for(int i=0;i<ve[u].size();i++){
int v=ve[u][i];
if(!vis[v]){
dfs(v);
dp[u][0]+=min(dp[v][1],min(dp[v][2],dp[v][0]));
dp[u][2]+=min(dp[v][0],dp[v][1]);
/*if(ve[v].size()==0){
dp[u][1]=0x3f3f3f3f;
return;
}*/
/*else{*/
dp[u][1]+=min(dp[v][0],dp[v][1]);
tmp=min(tmp,dp[v][0]-dp[v][1]);
/*}*/
}
}
if(tmp>=0){
dp[u][1]+=tmp;
}
}
类3:背包
题意
有一棵二叉苹果树,如果数字有分叉,一定是分两叉,即没有只有一个儿子的节点。这棵树共N个节点,标号1至N,树根编号一定为1。
我们用一根树枝两端连接的节点编号描述一根树枝的位置。一棵有四根树枝的苹果树,因为树枝太多了,需要剪枝。但是一些树枝上长有苹果,给定需要保留的树枝数量,求最多能留住多少苹果。
思路
与01背包类似,外层枚举总容量,内层枚举子树中选择留下的个数,dp[u][k]=min(dp[u][k-j]+dp[v][j-1]+w)
代码
void dfs(int u) {
vis[u]=1;
//dp[][]=;
for(int i=0;i<ve[u].size();i++){
int v=ve[u][i].first,w=ve[u][i].second;
//cout<<u<<' '<<v<<' '<<w<<endl;
if(!vis[v]){
dfs(v);
for(int k=q;k>=1;k--){
for(int j=1;j<=k;j++){
//cout<<dp[u][k]<<' '<<dp[u][k-j]<<' '<<dp[v][j-1]<<' '<<w<<endl;
if(dp[u][k]<dp[u][k-j]+dp[v][j-1]+w)
dp[u][k]=dp[u][k-j]+dp[v][j-1]+w;
}
}
}
}
}