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;
讲得已经十分详细了,剩下自己解决吧。