我现在很气。
气的理由和我那篇讲cqoi2017树形dp的博客差不多.....推dp方程推到想吐最后发现是个贪心。
我现在真的要因为树形dp怀疑人生了。
首先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
这个就非常恶心了.....而且也借鉴了贪心思想,不过比贪心程序跑的快些。当遇见看不懂的地方的时候,请读一读下面这句话:
前提:依据贪心思想,允许范围内,两个消防站距离越远,利用的越好,是更优决策。
我是看这这位大神的博客: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;
}