bzoj1217(洛谷P2279)消防局的设立(dp或贪心)

1.前言

我现在很气。

气的理由和我那篇讲cqoi2017树形dp的博客差不多.....推dp方程推到想吐最后发现是个贪心。

我现在真的要因为树形dp怀疑人生了。

2.贪心做法

首先dfs求出每一个节点的深度,然后深度从大到小排序(或者保存每个深度的所有节点),对于每一个不能被消防局覆盖到的节点,在它的爷爷节点上设置一个消防局最优,为什么呢?我不会理性的证明,只会感性的,感性的证明就三个字:显然嘛!

设置了以后,更新所有和爷爷节点距离为2以内的点的状态,注意!不要瞎剪枝(反正2003年的题数据范围很小)

#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
int read(){//读入优化是一种态度
	int w=1,q=0;char ch=' ';
	while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
	if(ch=='-')w=-1,ch=getchar();
	while(ch>='0'&&ch<='9')q=q*10+ch-'0',ch=getchar();
	return w*q;
}
int n,tot,dee,ans;
vectorcen[1005];
int ne[2005],h[1005],go[2005],fa[1005];//少用vector是一种态度
bool vis[1005];
void add(int x,int y){go[++tot]=y;ne[tot]=h[x];h[x]=tot;}
void dfs(int x,int las,int dep){
	dee=max(dee,dep);fa[x]=las;
	cen[dep].push_back(x);
	for(int i=h[x];i;i=ne[i])
		if(go[i]!=las)dfs(go[i],x,dep+1);
}
void bfs(int x,int bs,int las){
	vis[x]=1;if(bs==2)return;
	for(int i=h[x];i;i=ne[i])
		//if(!vis[go[i]])这句话是万恶之源(WA了两次)
		bfs(go[i],bs+1,x);
}
int main()
{
	int i,j,x;
	n=read();
	for(i=2;i<=n;i++){j=read();add(i,j);add(j,i);}
	dfs(1,0,1);
	for(i=dee;i>=1;i--)
		for(j=0;j

3.树形dp做法

这个就非常恶心了.....而且也借鉴了贪心思想,不过比贪心程序跑的快些。当遇见看不懂的地方的时候,请读一读下面这句话:

前提:依据贪心思想,允许范围内,两个消防站距离越远,利用的越好,是更优决策。

我是看这这位大神的博客:http://blog.csdn.net/qwsin/article/details/50954698

这样的:

f[i][0]表示在i处建立消防站。

f[i][1]表示在至少一个i的儿子处建立了消防站。

f[i][2]表示在至少一个i的孙子处建立了消防站。

-----那么以上三种情况i肯定是可以被消防站救火的------

f[i][3]表示i的所有儿子和孙子都可以被消防站救火(不一定消防站建在那里)

f[i][4]表示i的所有孙子都可以被救火。

-----以上情况i不一定可以被救火-----

对于f[i][0],其儿子和孙子都可以被覆盖(也就是能被救火,但不一定在上面有消防局),但是儿子的孙子就不行了,所以儿子的孙子必须保证可以被覆盖,所以:

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

对于f[i][1],我们选择了一个儿子后,所有儿子都可以被覆盖,但是不是所有孙子都会被覆盖。我们先让所有孙子被覆盖,然后选择一个点修改,现在重新读一读开头的前提,那么在孙子被覆盖,也就是f[i][4]情况里,所有儿子的状态都是3,所以要修改一个儿子的状态从3变成0:

f[i][1]=f[i][4]+min(f[son[i]][0]-f[son[i]][3]);

对于f[i][2],我们选择一个孙子后,不是所有儿子和孙子都可以被覆盖,那么再读一读前提,我们要在儿子和孙子都覆盖的情况下改变某个儿子的状态(从2改到1)

f[i][2]=f[i][3]+min(f[son[i]][1]-f[son[i]][2]);

对于f[i][3],每个儿子最远可以救火的地方,也就是在2状态下最优,读一读前提吧。

f[i][3]=Σf[son[i]][2];

对于f[i][4],读一读前提就知道了。

f[i][4]=Σf[son[i]][3];

代码:

#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
int read(){
	int w=1,q=0;char ch=' ';
	while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
	if(ch=='-')w=-1,ch=getchar();
	while(ch>='0'&&ch<='9')q=q*10+ch-'0',ch=getchar();
	return w*q;
}
int n,tot,inf=INT_MAX;
int go[2005],ne[2005],h[1005];
int f[1005][6];
void add(int x,int y){go[++tot]=y;ne[tot]=h[x];h[x]=tot;}
void dfs(int x,int las){
	int mx1=inf,mx2=inf;
	int i,bj=0;
	for(i=h[x];i;i=ne[i])
		if(go[i]!=las){
			dfs(go[i],x);
			f[x][0]+=f[go[i]][4];f[x][3]+=f[go[i]][2];f[x][4]+=f[go[i]][3];
			mx1=min(mx1,f[go[i]][0]-f[go[i]][3]);
			mx2=min(mx2,f[go[i]][1]-f[go[i]][2]);bj=1;
		}
	if(!bj){f[x][0]=f[x][1]=f[x][2]=1;f[x][3]=f[x][4]=0;}
	f[x][0]++;f[x][1]=f[x][4]+mx1;f[x][2]=f[x][3]+mx2;
	for(i=1;i<=4;i++)f[x][i]=min(f[x][i],f[x][i-1]);
}
int main()
{
	int i,j;
	n=read();
	for(i=2;i<=n;i++){j=read();add(i,j);add(j,i);}
	dfs(1,0);
	printf("%d",f[1][2]);
   	return 0;
} 


你可能感兴趣的:(动态规划,贪心)