洛谷 P2279 [HNOI2003]消防局的设立

P2279 [HNOI2003]消防局的设立

法一:

某贪心方法(摘自洛谷题解):一般的,对于深度最大的结点u,选择u的k级祖先是最划算的(意思是说这个题目的2改成了k我们都是可以做的,至于这个结论,详见刘汝佳的《***入门经典》(蓝书P35),还有一个例题,不过和本题不一样)

法二:

//树形dp
/*
状态的设计:
f[i][0]: 表示选了自己以后...
f[i][1]: 表示选了儿子以后...
f[i][2]: 表示选了孙子以后...
——上面用来表示这个点被覆盖了的状态,下面为这个点没有被覆盖的状态
f[i][3]: 表示自己不一定被覆盖,但是儿子一定全部被覆盖时...
f[i][4]: 表示自己和儿子都不一定被覆盖,但是孙子一定全部都被覆盖时...
...=>最少的消防局数 
i表示当前点,j表示所有有从i开始的边指向的点
f[i][0]=Σmin(f[j][0..4])+1
f[i][1]=min(f[k][0]+Σ(j!=k)min(f[j][0..3]))//k表示j中任何一个点,min(f[j][0..3])是由于没有选i、j点,而选了k点,因此j点能被覆盖,但如果要是j的子结点被覆盖,则需要0-3情况来满足
//min(f[k][0]..)是由于可能是任意一个儿子结点被选
f[i][2]=min(f[k][1]+Σ(j!=k)min(f[j][0..2])//可能是任意一个儿子结点的子节点被选
//由于没有选i、j、k点,j点没有被覆盖,要是它被覆盖则需要0-2情况
//举例:min(f[j][0..3])表示min(f[j][0],f[j][1],f[j][2],f[j][3])
f[i][3]=Σmin(f[j][0..2])
//所有的min都是对0..x生效,Σ对j生效
f[i][4]=Σmin(f[j][0..3])
简化: 
f[i][1]=min(Σmin(f[j][0..3])+f[k][0]-min(f[k][0..3]))=min(f[k][0]-min(f[k][0..3]))+Σmin(f[j][0..3])
f[i][2]=min(Σmin(f[j][0..2])+f[k][1]-min(f[k][0..2]))=min(f[k][1]-min(f[k][0..2]))+Σmin(f[j][0..2])
可以由此想到预处理令p[j][p]=min(f[j][0..p])(p>=2) (并非答案,只是方便计算)
则f[i][0]=Σp[j][4]+1
f[i][1]=Σp[j][3]+min(f[k][0]-p[k][3])
f[i][2]=Σp[j][2]+min(f[k][1]-p[k][2])
f[i][3]=Σp[j][2]
f[i][4]=Σp[j][3]
则
f[i][1]=f[i][4]+min(f[k][0]-p[k][3])
f[i][2]=f[i][3]+min(f[k][1]-p[k][2])
实际上,不用另开p数组,直接在f中存储即可,后面会直接覆盖掉
*/ 
//类似于最小支配集(在树中选出一些点,使得没有选出的点都与选出的点直接相连,要求选的点尽可能少)
#include<cstdio>
#include<algorithm>
using namespace std;
struct Edge
{
    int to,next;
}edge[2100];
int node[1100],edge_num,n;
int f[1100][5];
void make(int x,int y)
{
    edge[++edge_num].to=y;
    edge[edge_num].next=node[x];
    node[x]=edge_num;
}
void dfs(int x)
{
    int k=node[x],i,j,y,t1=0x3f3f3f3f,t2=0x3f3f3f3f;//优化版本 
    f[x][0]=1;
    while(k!=0)
    {
        y=edge[k].to;
        dfs(y);
        f[x][0]+=f[y][4];
        f[x][3]+=f[y][2];
        f[x][4]+=f[y][3];
        t1=min(t1,f[y][0]-f[y][3]);
        t2=min(t2,f[y][1]-f[y][2]);
        k=edge[k].next;
    }
    f[x][1]=f[x][4]+t1;
    f[x][2]=min(min(f[x][0],f[x][1]),t2+f[x][3]);
    f[x][3]=min(f[x][2],f[x][3]);
    f[x][4]=min(f[x][3],f[x][4]);
//    int k=node[x],i,j,y;
//    while(k!=0)
//    {
//        y=edge[k].to;
//        dfs(y);
//        for(i=2;i<=4;i++)
//            for(j=0;j<i;j++)
//                f[y][i]=min(f[y][i],f[y][j]);
//        f[x][0]+=f[y][4];
//        f[x][3]+=f[y][2];
//        f[x][4]+=f[y][3];
//        k=edge[k].next;
//    }
//    f[x][0]++;
//    k=node[x];
//    f[x][1]=0x3f3f3f3f;f[x][2]=0x3f3f3f3f;
//    while(k!=0)
//    {
//        y=edge[k].to;
//        f[x][1]=min(f[x][1],f[y][0]-f[y][3]);
//        f[x][2]=min(f[x][2],f[y][1]-f[y][2]);
//        k=edge[k].next;
//    }
//    f[x][1]+=f[x][4];
//    f[x][2]+=f[x][3];
}
int main()
{
    int i,t;
    scanf("%d",&n);
    for(i=2;i<=n;i++)
    {
        scanf("%d",&t);
        make(t,i);
    }
    dfs(1);
    printf("%d",f[1][2]); //此时f[1][2]已经是min(f[1][0..2])了
    return 0;
}

原文地址:https://www.cnblogs.com/hehe54321/p/8470446.html