您的位置:首页 > 其它

P2279 [HNOI2003]消防局的设立

2018-02-10 09:53 253 查看

题目描述

2020年,人类在火星上建立了一个庞大的基地群,总共有n个基地。起初为了节约材料,人类只修建了n-1条道路来连接这些基地,并且每两个基地都能够通过道路到达,所以所有的基地形成了一个巨大的树状结构。如果基地A到基地B至少要经过d条道路的话,我们称基地A到基地B的距离为d。

由于火星上非常干燥,经常引发火灾,人类决定在火星上修建若干个消防局。消防局只能修建在基地里,每个消防局有能力扑灭与它距离不超过2的基地的火灾。

你的任务是计算至少要修建多少个消防局才能够确保火星上所有的基地在发生火灾时,消防队有能力及时扑灭火灾。

输入输出格式

输入格式:

输入文件名为input.txt。

输入文件的第一行为n (n<=1000),表示火星上基地的数目。接下来的n-1行每行有一个正整数,其中文件第i行的正整数为a[i],表示从编号为i的基地到编号为a[i]的基地之间有一条道路,为了更加简洁的描述树状结构的基地群,有a[i]<i。

输出格式:

输出文件名为output.txt

输出文件仅有一个正整数,表示至少要设立多少个消防局才有能力及时扑灭任何基地发生的火灾。

输入输出样例

输入样例#1: 复制
6
1
2
3
4
5


输出样例#1:
2

专门学习下树形dp的解法,不用贪心
真的绕晕我了,第一次做树形dp,一定要写详细点

首先说dp[i][j](0<=j<=4)定义:以i为根向下的整个子树,以j方式覆盖i节点的,满足整个子树都能灭火的最少消防局的个数

然后我们来探讨有多少种方式可以更新覆盖A呢?




应该是5种:
选A(dp[i][0]),选A儿子B1(dp[i][1]),选A孙子B2(dp[i][2]),选A爷爷C2(dp[i][3]),选A爸爸C1(dp[i][4])

但最后两种因为不在A子树内,是被A子树外的节点覆盖。所以与儿子或者孙子形成了分隔




最后我们分析状态转移方程:(树形dp死死抓住一点不放,一定是儿子(所有状态)更新爸爸(所有状态))

(j,k为A的儿子B中的某一个)

1.A被自己子树内元素更新:
a.dp[i][0] 选A
  其原有效果是B,C都被访问到了,D木有访问



 所以用dp[j][0~4]儿子的状态来更新
  即 dp[i][0] = 1+Σmin(dp[j][0~4]);
b.dp[i][1] 选B
  假设先选了k=B1,其对应的是dp[k][0](dp[k][0]一定得到了以B1为根的子树的最少消防局个数)

  其原有效果是B1子树,A,B2,B3被访问,但是(红色区域)C3~C6,D5~D9都木有被访问,所以去访问这些未访问的点



所以孙子必须被访问

状态转移方程:
dp[i][1] = min( dp[k][0] + Σ(j != k)min(dp[j][0~3]) );

c.dp[i][2] 选C
  假设先选了C1,其对应的dp[k][1] (k=B1)
(我之前一直困扰的一点就是为何D3,D4不需要用来再访问一次,事实上是概念还是没搞清白,dp[k][1]本身定义就是B1这个子树被选C1这种方式已经完成了所有的灭火工作了(注意:dp[k][3],dp[k][4]就没有这个特性了)!!!所以当然D3,D4被访问过了啊)
  其原有效果是B1子树,A被访问,但是红色区域未被访问



所以儿子必须被访问

状态转移方程:

dp[i][2] = min( dp[k][1] + Σ(j != k)min(dp[j][0~2]) );

2. A被子树外上面的节点更新
a.选爷爷
  dp[i][3] = Σdp[j][0~2]; //儿子要遍历



b.选爸爸
  dp[i][4] = Σdp[j][0~3]; //孙子要遍历



优化处理:

因为每种转移方程至少有三种可能最后取其中较小的 故时间效率较低 令dp[i][k]表示min(dp[i][0],dp[i][1]....dp[i][k])且k>=2 因为上述转移方程最少都是0~2状态

那么转移方程就大幅度化简了:

注意:因为从下而上,这里儿子dp[j][]是已经min简化过了的,那么我们先算未简化版的dp[i][],之后再专门min简化处理

先算这三个(直接由上面变形而来 )

dp[i][0] = 1+Σdp[j][4];

dp[i][3] = Σdp[j][2];

dp[i][4] = Σdp[j][3];

这两个有些部分可以直接拿dp[i][3],dp[i][4]替换了,但注意要减掉dp[k][3]的这个部分(j!=k)

dp[i][1] = dp[i][4] + min(dp[k][0]-dp[k][3]);

dp[i][2] = dp[i][3] + min(dp[k][1]-dp[k][2]);

最后上代码:


#include<bits/stdc++.h>
using namespace std;
#define maxn 1005
typedef long long ll;
#define inf 9999999999
#define re register
struct node
{
int next,to;
} e[maxn];
int head[maxn],tot=0;
int dp[maxn][5];
int n;
void add(int u,int v)
{
tot++;
e[tot].to=v;
e[tot].next=head[u];
head[u]=tot;
}
void dfs(int u)
{
dp[u][0]=1;
int x1=inf,x2=inf;
for(int p=head[u]; p; p=e[p].next)
{
int v=e[p].to;
dfs(v);
dp[u][0]+=dp[v][4];
dp[u][3]+=dp[v][2];
dp[u][4]+=dp[v][3];
x1=min(x1,dp[v][0]-dp[v][3]);
x2=min(x2,dp[v][1]-dp[v][2]);
}
dp[u][1]=min(dp[u][4]+x1,dp[u][0]);
dp[u][2]=min(dp[u][3]+x2,dp[u][1]);
dp[u][3]=min(dp[u][3],dp[u][2]);
dp[u][4]=min(dp[u][4],dp[u][3]);
}

int main()
{
//    freopen("test.txt","r",stdin);
cin>>n;
for(int i=2; i<=n; i++)
{
int x;
cin>>x;
add(x,i);
}
dfs(1);
cout<<dp[1][2];

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: