您的位置:首页 > 其它

BZOJ 1217: [HNOI2003]消防局的设立 树形dp/贪心

2017-11-07 17:15 561 查看

1217: [HNOI2003]消防局的设立

Time Limit: 10 Sec  Memory Limit: 162 MB
Submit: 818  Solved: 468

[Submit][Status][Discuss]

Description

2020年,人类在火星上建立了一个庞大的基地群,总共有n个基地。起初为了节约材料,人类只修建了n-1条道路来连接这些基地,并且每两个基地都能够通过道路到达,所以所有的基地形成了一个巨大的树状结构。如果基地A到基地B至少要经过d条道路的话,我们称基地A到基地B的距离为d。由于火星上非常干燥,经常引发火灾,人类决定在火星上修建若干个消防局。消防局只能修建在基地里,每个消防局有能力扑灭与它距离不超过2的基地的火灾。你的任务是计算至少要修建多少个消防局才能够确保火星上所有的基地在发生火灾时,消防队有能力及时扑灭火灾。

Input

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

Output

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

Sample Input

6

1

2

3

4

5

Sample Output

2

树形dp做法

用f[i][j]表示i的子树中所有点已被覆盖,且还能向上覆盖j层的最小花费

用g[i][j]表示i没有被覆盖,且下面j层也有没被覆盖的最小花费

#include<cmath>
#include<ctime>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<iomanip>
#include<vector>
#include<string>
#include<bitset>
#include<queue>
#include<map>
#include<set>
using namespace std;

inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch<='9'&&ch>='0'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return x*f;
}
void print(int x)
{if(x<0)putchar('-'),x=-x;if(x>=10)print(x/10);putchar(x%10+'0');}

const int N=1010;

int last
,ecnt;
struct EDGE{int to,nt;}e[N<<1];
inline void add(int u,int v)
{e[++ecnt]=(EDGE){v,last[u]};last[u]=ecnt;}

int fa
;

int f
[3],g
[2];

void dfs(int u)
{
f[u][0]=f[u][1]=f[u][2]=1;
for(int v,i=last[u];i;i=e[i].nt)
if(e[i].to!=fa[u])
{
v=e[i].to;
fa[v]=u;
dfs(v);
f[u][0]=min(f[u][0]+f[v][0],g[u][0]+f[v][1]);
f[u][1]=min(f[u][1]+g[v][0],g[u][1]+f[v][2]);
f[u][2]+=g[v][1];
g[u][0]+=f[v][0];
g[u][1]+=g[v][0];
f[u][1]=min(f[u][1],f[u][2]);
f[u][0]=min(f[u][0],f[u][1]);
g[u][0]=min(f[u][0],g[u][0]);
g[u][1]=min(g[u][1],g[u][0]);
}
}

int main()
{
int n=read();
for(int v,i=2;i<=n;++i)
{
v=read();
add(i,v);add(v,i);
}
dfs(1);
cout<<f[1][0]<<endl;
return 0;
}
/*
6
1
2
3
4
5

2
用f[i][j]表示i的子树中所有点已被覆盖,且还能向上覆盖j层的最小花费
用g[i][j]表示i没有被覆盖,且下面j层也有没被覆盖的最小花费
*/


贪心就是令两个消防站的距离恰好为4就好

#include<cmath>
#include<ctime>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<iomanip>
#include<vector>
#include<string>
#include<bitset>
#include<queue>
#include<map>
#include<set>
using namespace std;

typedef long long ll;

inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch<='9'&&ch>='0'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return x*f;
}
void print(int x)
{if(x<0)putchar('-'),x=-x;if(x>=10)print(x/10);putchar(x%10+'0');}

const int N=1010,inf=0X3f3f3f3f;

int last
,ecnt;
struct EDGE{int to,nt;}e[N<<1];
inline void add(int u,int v)
{e[++ecnt]=(EDGE){v,last[u]};last[u]=ecnt;}

int n,ans;
int fa
,f
;

void dfs(int u)
{
int mx=-inf,mn=inf;
for(int v,i=last[u];i;i=e[i].nt)
if(e[i].to^fa[u])
{
v=e[i].to;
fa[v]=u;
dfs(v);
mx=max(mx,f[v]);
mn=min(mn,f[v]);
}
mn+mx<=3?f[u]=mn+1:f[u]=mx+1;
if(mx==-inf)f[u]=3;
if(f[u]==5)
{
f[u]=0;
ans++;
}
if(f[u]>=3&&!fa[u])ans++;
}

int main()
{
n=read();
for(int i=2,u;i<=n;++i)
{
u=read();
add(u,i);add(i,u);
}
dfs(1);
cout<<ans<<endl;
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: