洛谷2279:消防局的设立

洛谷2279:消防局的设立

题意:

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

数据范围:

  • (1leq nleq 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;
}

原文地址:https://www.cnblogs.com/zxytxdy/p/12049773.html