zoukankan      html  css  js  c++  java
  • 洛谷 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;
    }

  • 相关阅读:
    async简单使用
    node调用phantomjs-node爬取复杂页面
    mongodb3 ubuntu离线安装(非apt-get)及用户管理
    2040-亲和数(java)
    JavaScript闭包简单理解
    nodejs构建多房间简易聊天室
    linux下安装nodejs及npm
    EventBus轻松使用
    mysql用户创建及授权
    python中json的基本使用
  • 原文地址:https://www.cnblogs.com/hehe54321/p/8470446.html
Copyright © 2011-2022 走看看