洛谷2279:消防局的设立

洛谷2279:消防局的设立

题意:

  • 给定一棵树,树上的某些节点设立消防局,这个消防局可以覆盖距离不超过2的节点。
  • 问有最少有多少个消防局可以覆盖整棵树。

数据范围:

  • \(1\leq n\leq 1000\)

思路:

  • 树形\(dp\)
  • \(f(i,state)\)表示节点\(i\)\(state\)状态下表示最小的消防站个数。
  • \(f(i,0)\)表示选择\(i\)为消防局。
  • \(f(i,1)\)表示至少选了一个\(i\)的一个儿子为消防局。
  • \(f(i,2)\)表示至少选了一个\(i\)的孙子为消防局。
    • 这三种情况\(i\)可以覆盖自己。
  • \(f(i,3)\)表示\(i\)的所有儿子节点一定被消防局覆盖。
  • \(f(i,4)\)表示\(i\)的所有孙子节点一定被消防局覆盖。
  • 转移方程:
    • \(f(i,0)=1+\sum min(f(j,0...4))\)
      • 解释:由于\(i\)为消防站儿子和孙子就无所谓了,因此从0~4中寻找一个\(min\)来转移,最后\(+1\)(自己的开销)。这里\(j\)对应的是儿子。
    • \(f(i,1)=min(f(k,0)+\sum_{j!=k}min(f(j,0...3))\)
      • 解释:\(f(i,1)\)表示至少选了一个\(i\)的儿子为消防局,那么不能保证所有的孙子都能被覆盖,只能保证父亲和所有兄弟被覆盖到,同时需要\(i\)的孙子一定要被全部覆盖。\(i\)的孙子被全部覆盖,状态0~3满足。这里\(k\)对应的是其中一个儿子,\(j\)对应的是除了\(k\)以外的其他儿子。
    • \(f(i,2)=min(f(k,1)+\sum_{j!=k}min(f(j,0...2))\)
      • 解释:选了一个孙子,那么所有的儿子应该被全部覆盖掉,儿子被覆盖的状态对应0~2。这里\(k\)对应的是其中一个儿子,\(j\)对应的是除了\(k\)以外的其他儿子。
    • \(f(i,3)=\sum min(f(j,0...2))\)
      • 解释:要让所有的儿子一定被覆盖,那么儿子的0~2状态都符合条件。
    • \(f(i,4)=\sum min(f(j,0...3))\)
      • 解释:孙子全部被覆盖,那么儿子的0~3状态都符合条件,所以取个\(min\)
  • 发现了所有的状态都是0~?这样的形式,所以令\(f(i,j)\)表示\(min(f(i,0...k))\)
  • 因此有:
    • \(f(i,0)=1+\sum f(j,4)\).
    • \(f(i,1)=f(i,4)+min(f(j,0)-f(j,3))\).
      • 选一个儿子作为消防局,需要保证所有孙子应该被覆盖,所以加上\(f(i,4)\)。对于\(f(i,4)\),选那个儿子处于状态0~3,但是要把他转变为只选择自己,所以取\(f(j,0)-f(j,3)\)
    • \(f(i,2)=f(i,3)+min(f(j,1)-f(j,2))\).
      • 选一个孙子作为消防局,需要保证所有儿子被覆盖,所以加上 \(f(i,3)\)。对于\(f(i,3)\),儿子处于\(f(j,2)\)状态,但是需要将一个儿子变为0~1状态保证父亲节点被覆盖,所以取\(f(j,1)-f(j,2)\)
    • \(f(i,3)=\sum f(j,2)\).
    • \(f(i,4)=\sum f(j,3)\).
#include<bits/stdc++.h>
using namespace std;

const int maxn = 1000 + 10;
const int INF = 0x3f3f3f3f;
int a[maxn], n;
bool G[maxn][maxn];
int f[maxn][10];

int main()
{
    scanf("%d", &n);
    for(int i = 2, x; i <= n; i++)
    {
        scanf("%d", &x);
        G[x][i] = 1;
    }
    for(int i = n, x1, x2; i >= 1; i--)
    {
        x1 = x2 = INF;
        f[i][0] = 1;
        for(int j = 1; j <= n; j++)
        {
            if(G[i][j])
            {
                f[i][0] += f[j][4];
                f[i][3] += f[j][2];
                f[i][4] += f[j][3];
                x1 = min(x1, f[j][0]-f[j][3]);
                x2 = min(x1, f[j][1]-f[j][2]);
            }
        }
        f[i][1] = f[i][4] + x1;
        f[i][2] = min(f[i][3]+x2, min(f[i][0], f[i][1]));
        f[i][3] = min(f[i][2], f[i][3]);
        f[i][4] = min(f[i][3], f[i][4]);
    }
    cout << f[1][2] << endl;
    return 0;
}

posted @ 2019-12-16 16:29  zhaoxiaoyun  阅读(181)  评论(0编辑  收藏  举报