LOJ-皇宫看守

题目

题目链接

测试得分:  100

主要算法 :  树型DP、点的最小覆盖,二分图(匈牙利算法)

题干:

   点加权的最小覆盖

应试策略:

  题目说的很清楚,用最少的点覆盖所有的点。题目给出的是个树,所以可以用动态规划来解决。
  给出如下定义:
  F[i,0]表示i点不放,i可以被父亲节点观察到;
  F[i,1]表示i点不放,i可以被儿子节点观察到;
  F[i,2]表示i点放,在i处设置警卫;
  转移如下:
    1、由F[i,0]定义可知,设j为i的儿子节点,儿子节点都需要被观察到,但由于根节点i不放,所以儿子必须保证能被观察到,即F[j][1],F[j][2],所以我们需要枚举必须放置的儿子节点,即下:
      F[i,0] = ∑(min(F[j][1],F[j,2]))}
        其中i为枚举儿子节点

    2、由F[i,1]定义可知,i的儿子结点中一定有一个结点有直接警卫,其它儿子节点必须都要被观察到,即下:
      F[i,1] =min(∑(min(F[j,1],F[j,2])+F[k,2])
        其中k为枚举的必放的儿子节点,j为除了k之外的儿子节点
    3、由F[i,2]定义可知,i点放置了守卫,所以对于每个儿子节点都能被观察到,取F[j,0],F[j,1],F[j,2]最小值即可,即下:
      F[i,2] = min(F[j,0],F[j,1],F[j,2])
        j是i的儿子节点

  代码

/*题目说的很清楚,用最少的点覆盖所有的点。题目给出的是个树,所以可以用动态规划来解决。
    给出如下定义:
        F[i,0]表示i点不放,i可以被父亲节点观察到;
        F[i,1]表示i点不放,i可以被儿子节点观察到;
        F[i,2]表示i点放,在i处设置警卫;
    转移如下:
    1、由F[i,0]定义可知,设j为i的儿子节点,儿子节点都需要被观察到,但由于根节点i不放,所以儿子必须保证能被观察到,即F[j][1],F[j][2],所以我们需要枚举必须放置的儿子节点,即下:
          F[i,0] = ∑(min(F[j][1],F[j,2]))}
            其中i为枚举儿子节点

    2、由F[i,1]定义可知,i的儿子结点中一定有一个结点有直接警卫,其它儿子节点必须都要被观察到,即下:
          F[i,1] =min(∑(min(F[j,1],F[j,2])+F[k,2])  
            其中k为枚举的必放的儿子节点,j为除了k之外的儿子节点
    3、由F[i,2]定义可知,i点放置了守卫,所以对于每个儿子节点都能被观察到,取F[j,0],F[j,1],F[j,2]最小值即可,即下:
          F[i,2] = min(F[j,0],F[j,1],F[j,2]) 
              j是i的儿子节点*/

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define FORa(i,s,e) for(int i=s;i<=e;i++)
#define FORs(i,s,e) for(int i=s;i>=e;i--)

using namespace std;
const int N=1500,M=1500,INF=2147483647;
int n,m,root,num_edge,head[N+1],a[N+1],f[N+1][3];
/*f[u][0]表示的是结点u可以被父亲看到的安排的u为树根的子树安排的最少的警卫数
 f[u][1]表示的是结点u可以被儿子看到的安排的u为树根的子树安排的最少的警卫数
 f[u][2]表示的是结点u上安排警卫时的u为树根的子树安排的最少的警卫数
*/
struct Edge{
    int next,to;
}edge[2*M+2]; 
void Add_edge(int from,int to) {edge[++num_edge]=(Edge){head[from],to},head[from]=num_edge;}
inline int min(int fa,int fb){return fa<fb?fa:fb;}
void Dp(int u,int fa)
{
    int d=INF;
    for(int i=head[u];i;i=edge[i].next)
    {
        int v=edge[i].to;
        if(v!=fa)
        {
            Dp(v,u);
            f[u][0]+=min(f[v][1],f[v][2]);
            f[u][1]+=min(f[v][1],f[v][2]);
            d=min(d,f[v][2]-min(f[v][1],f[v][2]));
            f[u][2]+=min(f[v][0],min(f[v][1],f[v][2]));
        }
    }
    f[u][1]+=d,f[u][2]+=a[u];
    /*状态转移方程
        当点u可以被父亲看到的安排的u为树根的子树安排的最少的警卫数为保证子结点能够全部观察到,则为min(f[v][1],f[v][2])的和
         当点u可以被儿子看到的安排的u为树根的子树安排的最少的警卫数为保证子结点能够全部观察到,则至少有一个儿子结点必须要选,F[i,0] = min{∑(min(F[j][0],F[j,2]))+F[k,2]}
        当点u上安排警卫时的u为树根的子树安排的最少的警卫数为儿子结点的三者的min,F[i,2] = min(F[j,0],F[j,1],F[j,2]) 
    */ 
}
int main()
{
    int from,to,fcnt;
    scanf("%d",&n);
    FORa(i,1,n)
    {
        scanf("%d",&from),scanf("%d%d",&a[from],&fcnt);
        FORa(j,1,fcnt) scanf("%d",&to),Add_edge(from,to),Add_edge(to,from);
    }
    root=1,Dp(root,0);
    printf("%d",min(f[root][1],f[root][2]));
    return 0;
}
/*6
1 30 3 2 3 4
2 16 2 5 6
3 5 0
4 4 0
5 11 0
6 5 0*/

总结:

  1.确定建立模型

  2.构建知识架构,模型体系

题目说的很清楚,用最少的点覆盖所有的点。题目给出的是个树,所以可以用动态规划来解决。
     给出如下定义:
F[i,0]表示i点不放,且以i为根节点的子树(包括i节点)全部被观察到;
F[i,1]表示i点不放,且以i为根节点的子树(可以不包括i节点)全部被观察到;
F[i,2]表示i点放,且以i为根节点的子树全部被观察到;
     转移如下:
1、由F[i,0]定义可知,设j为i的儿子节点,至少要有一个i的儿子节点是放置守卫的,其余的儿子节点可放可不放,但由于根节点i不放,所以其余的儿子节点如果不放的话,必须保证能被观察到,即F[j][0];所以我们需要枚举必须放置的儿子节点,即下:

  F[i,0] = min{∑(min(F[j][0],F[j,2]))+F[k,2]}

其中k为枚举的必放的儿子节点,j为除了k之外的儿子节点


2、由F[i,1]定义可知,i可以被观察到也可以不被观察到,但儿子节点必须都要被观察到,即下:

  F[i,1] =∑(min(F[j,0],F[j,2])) 

  j是i的儿子节点


3、由F[i,2]定义可知,i点放置了守卫,所以对于每个儿子节点都能被观察到,取F[j,0],F[j,1],F[j,2]最小值即可,即下:
  F[i,2] = min(F[j,0],F[j,1],F[j,2]) j是i的儿子节点

对于叶节点i,F[i,0] = F[i,2] = data[i],F[i,1] = 0;

讲得已经十分详细了,剩下自己解决吧。

猜你喜欢

转载自www.cnblogs.com/SeanOcean/p/11314323.html