树的最小支配集 E - Cell Phone Network POJ - 3659 E. Tree with Small Distances E. Paint the Tree 树形dp

E - Cell Phone Network

 POJ - 3659 

题目大意:

给你一棵树,放置灯塔,每一个节点可以覆盖的范围是这个节点的所有子节点和他的父亲节点,问要使得所有的节点被覆盖的最少灯塔数量。

考虑每一个节点要被覆盖应该如何放置灯塔。

如果一个节点被覆盖 1 该节点放了灯塔  2 该点的父亲节点放了灯塔  3 该点的儿子节点放了灯塔。

dp[u][0] 表示这个节点的儿子节点放了灯塔

dp[u][1] 表示这个点本身放了灯塔

dp[u][2] 表示这个点的父亲节点放了灯塔

转移方程,

dp[u][1] 可以从儿子的三个状态转移 dp[u][1]=min(dp[v][0],dp[v][1],dp[v][2])

dp[u][2] 那么如果要儿子节点被覆盖,要么儿子本身有灯塔,要么儿子的儿子有灯塔 dp[u][2]=min(dp[v][0],dp[v][1]) 

扫描二维码关注公众号,回复: 7441540 查看本文章

dp[u][0] 这个是这个节点的儿子节点放了灯塔,但是如果这个节点有很多个儿子,我们只要其中一个即可

所以这个转移比较复杂,可以像之前的 E. Paint the Tree 树形dp 这个一样的去处理。

不过这个对于子节点选dp[v][1]的限制是只要一个即可,所以可以用一个更简单的方法。

设置一个变量dif,dif=min(dp[v][1]-dp[v][0],dif)

最后在加上这个dif即可,其实两个本质上是一样的。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <stack>
#include <bitset>
#include <vector>
#include <map>
#include <string>
#include <cstring>
#include <bitset>
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=3e5+10;
typedef long long ll;
vector<int>G[maxn];
void add(int u,int v){
    G[u].push_back(v);
    G[v].push_back(u);
}
int n;
void read(){
    scanf("%d",&n);
    for(int i=1;i<n;i++){
        int u,v;
        scanf("%d%d",&u,&v);
        add(u,v);
    }
}
int dp[maxn][3];
void dfs(int u,int pre){
    dp[u][0]=0;
    dp[u][1]=1;
    dp[u][2]=0;
    for(int i=0;i<G[u].size();i++){
        int v=G[u][i];
        if(v==pre) continue;
        dfs(v,u);
        dp[u][1]+=min(dp[v][1],min(dp[v][0],dp[v][2]));
        dp[u][2]+=min(dp[v][1],dp[v][0]);
        dp[u][0]+=dp[v][0];
    }
    vector<int>val;val.clear();
    for(int i=0;i<G[u].size();i++){
        int v=G[u][i];
        if(v==pre) continue;
        val.push_back(dp[v][1]-dp[v][0]);
    }
    if(val.size()==0) dp[u][0]=inf;
    sort(val.begin(),val.end());
    if(val.size()&&val[0]>0) dp[u][0]+=val[0];
    else {
        for(int i=0;i<val.size();i++){
            if(val[i]>0) break;
            dp[u][0]+=val[i];
        }
    }
    // printf("dp[%d][0]=%d dp[%d][1]=%d dp[%d][2]=%d\n",u,dp[u][0],u,dp[u][1],u,dp[u][2]);
}

int main(){
    read();
    dfs(1,-1);
    printf("%d\n",min(dp[1][0],dp[1][1]));
    return 0;
}
View Code

E. Tree with Small Distances

题目差不多。

题目大意:

给你一棵树,要求这棵树的根节点1 到 每一个点的距离要小于等于2 增加的最少的路数。

仔细比划比划 就发现和上面是一样的题目。

只是要标记一下本来就和根节点1 距离小于等于2的所有节点,这些节点的转移有一点不一样,其他都是一样的。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <stack>
#include <bitset>
#include <vector>
#include <map>
#include <string>
#include <cstring>
#include <bitset>
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=2e5+10;
typedef long long ll;
vector<int>G[maxn];
int vis[maxn];
void add(int u,int v){
    G[u].push_back(v);
    G[v].push_back(u);
}
int n;
void read(){
    scanf("%d",&n);
    for(int i=1;i<n;i++){
        int u,v;
        scanf("%d%d",&u,&v);
        add(u,v);
    }
}
int dp[maxn][3];
void dfs(int u,int pre){
    dp[u][0]=0;
    dp[u][1]=1;
    dp[u][2]=0;
    int dif=inf;
    for(int i=0;i<G[u].size();i++){
        int v=G[u][i];
        if(v==pre) continue;
        dfs(v,u);
        if(vis[u]){
            dp[u][0]+=min(dp[v][1],dp[v][0]);
            dp[u][1]+=min(dp[v][1],min(dp[v][0],dp[v][2]));
            dp[u][2]+=min(dp[v][1],dp[v][0]);
        }
        else{
            dp[u][1]+=min(dp[v][1],min(dp[v][0],dp[v][2]));
            dp[u][2]+=min(dp[v][1],dp[v][0]);
            dp[u][0]+=min(dp[v][1],dp[v][0]);
            dif=min(dp[v][1]-min(dp[v][1],dp[v][0]),dif);
        }
    }
    if(vis[u]) return ;
    dp[u][0]+=dif;
}

void init(int u,int pre){
    vis[u]=1;
    for(int i=0;i<G[u].size();i++){
        int v=G[u][i];
        vis[v]=2;
        for(int j=0;j<G[v].size();j++){
            int x=G[v][j];
            vis[x]=3;
        }
    }
}

int main(){
    read();
    init(1,-1);
    dfs(1,-1);
    printf("%d\n",min(dp[1][0],dp[1][1]));
    return 0;
}

 
View Code

猜你喜欢

转载自www.cnblogs.com/EchoZQN/p/11644524.html