[BZOJ1596][Usaco2008 Jan]电话网络(贪心||树形dp)

题目描述

传送门

题解

贪心:如果一个点被覆盖,那么它的父亲和所有儿子都没必要被覆盖;如果一个点的父亲被覆盖,那么这个点以及它所有的兄弟都没必要被覆盖。所以可以遍历每一个点,如果这个点所有的儿子都没被覆盖并且这个点也没被覆盖,就将它的父亲覆盖,答案+1。
dp:
f[i][0]:以i为根的子树中所有点均被覆盖且草地i上无信号塔所需的最小塔数(i被其儿子覆盖)
f[i][1]:以i为根的子树中所有点均被覆盖且草地i上有信号塔所需的最小塔数
f[i][2]:以i为根的子树中除i点以外其余点均被覆盖所需的最小塔数

代码

贪心

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int max_n=1e4+5;
const int max_e=max_n*2;
int n,x,y,ans;int tot,point[max_n],next[max_e],v[max_e];
bool vis[max_n];
inline void add(int x,int y){++tot;next[tot]=point[x];point[x]=tot;v[tot]=y;}
inline void dfs(int x,int fa){
    bool flag=false;
    for (int i=point[x];i;i=next[i])
      if (v[i]!=fa){
        dfs(v[i],x);
        if (vis[v[i]]) flag=true;
      }
    if (!flag&&!vis[x]&&!vis[fa]) vis[fa]=true,ans++;
}
int main(){
    scanf("%d",&n);
    for (int i=1;i<n;++i) scanf("%d%d",&x,&y),add(x,y),add(y,x);
    dfs(1,0);
    printf("%d\n",ans);
}

dp

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
#define LL long long
const int max_n=1e4+5;
const int max_e=max_n*2;
const LL INF=2e9;
int n,x,y; LL f[max_n][3];
int tot,point[max_n],next[max_e],v[max_e];
inline void add(int x,int y){++tot;next[tot]=point[x];point[x]=tot;v[tot]=y;}
void treedp(int x,int fa){
    LL sum=0;
    f[x][1]=1;f[x][0]=INF;
    for (int i=point[x];i;i=next[i])
    if (v[i]!=fa){
        treedp(v[i],x);
        f[x][1]+=min(f[v[i]][0],min(f[v[i]][1],f[v[i]][2]));
        f[x][2]+=f[v[i]][0];
        sum+=min(f[v[i]][0],f[v[i]][1]);
    }
    for (int i=point[x];i;i=next[i])
    if (v[i]!=fa)
      f[x][0]=min(f[x][0],f[v[i]][1]+sum-min(f[v[i]][0],f[v[i]][1]));
}
int main(){
    scanf("%d",&n);
    for (int i=1;i<n;++i) scanf("%d%d",&x,&y),add(x,y),add(y,x);
    treedp(1,0);
    printf("%lld\n",min(f[1][0],f[1][1]));
}

总结

感觉这种覆盖一类的题往往可以拆成三个状态。。。

你可能感兴趣的:(dp,bzoj)