洛谷 P2279 [HNOI2003]消防局的设立
法一:
某贪心方法(摘自洛谷题解):一般的,对于深度最大的结点u,选择u的k级祖先是最划算的(意思是说这个题目的2改成了k我们都是可以做的,至于这个结论,详见刘汝佳的《***入门经典》(蓝书P35),还有一个例题,不过和本题不一样)
法二:
//树形dp /* 状态的设计: f[i][0]: 表示选了自己以后... f[i][1]: 表示选了儿子以后... f[i][2]: 表示选了孙子以后... ——上面用来表示这个点被覆盖了的状态,下面为这个点没有被覆盖的状态 f[i][3]: 表示自己不一定被覆盖,但是儿子一定全部被覆盖时... f[i][4]: 表示自己和儿子都不一定被覆盖,但是孙子一定全部都被覆盖时... ...=>最少的消防局数 i表示当前点,j表示所有有从i开始的边指向的点 f[i][0]=Σmin(f[j][0..4])+1 f[i][1]=min(f[k][0]+Σ(j!=k)min(f[j][0..3]))//k表示j中任何一个点,min(f[j][0..3])是由于没有选i、j点,而选了k点,因此j点能被覆盖,但如果要是j的子结点被覆盖,则需要0-3情况来满足 //min(f[k][0]..)是由于可能是任意一个儿子结点被选 f[i][2]=min(f[k][1]+Σ(j!=k)min(f[j][0..2])//可能是任意一个儿子结点的子节点被选 //由于没有选i、j、k点,j点没有被覆盖,要是它被覆盖则需要0-2情况 //举例:min(f[j][0..3])表示min(f[j][0],f[j][1],f[j][2],f[j][3]) f[i][3]=Σmin(f[j][0..2]) //所有的min都是对0..x生效,Σ对j生效 f[i][4]=Σmin(f[j][0..3]) 简化: f[i][1]=min(Σmin(f[j][0..3])+f[k][0]-min(f[k][0..3]))=min(f[k][0]-min(f[k][0..3]))+Σmin(f[j][0..3]) f[i][2]=min(Σmin(f[j][0..2])+f[k][1]-min(f[k][0..2]))=min(f[k][1]-min(f[k][0..2]))+Σmin(f[j][0..2]) 可以由此想到预处理令p[j][p]=min(f[j][0..p])(p>=2) (并非答案,只是方便计算) 则f[i][0]=Σp[j][4]+1 f[i][1]=Σp[j][3]+min(f[k][0]-p[k][3]) f[i][2]=Σp[j][2]+min(f[k][1]-p[k][2]) f[i][3]=Σp[j][2] f[i][4]=Σp[j][3] 则 f[i][1]=f[i][4]+min(f[k][0]-p[k][3]) f[i][2]=f[i][3]+min(f[k][1]-p[k][2]) 实际上,不用另开p数组,直接在f中存储即可,后面会直接覆盖掉 */ //类似于最小支配集(在树中选出一些点,使得没有选出的点都与选出的点直接相连,要求选的点尽可能少) #include<cstdio> #include<algorithm> using namespace std; struct Edge { int to,next; }edge[2100]; int node[1100],edge_num,n; int f[1100][5]; void make(int x,int y) { edge[++edge_num].to=y; edge[edge_num].next=node[x]; node[x]=edge_num; } void dfs(int x) { int k=node[x],i,j,y,t1=0x3f3f3f3f,t2=0x3f3f3f3f;//优化版本 f[x][0]=1; while(k!=0) { y=edge[k].to; dfs(y); f[x][0]+=f[y][4]; f[x][3]+=f[y][2]; f[x][4]+=f[y][3]; t1=min(t1,f[y][0]-f[y][3]); t2=min(t2,f[y][1]-f[y][2]); k=edge[k].next; } f[x][1]=f[x][4]+t1; f[x][2]=min(min(f[x][0],f[x][1]),t2+f[x][3]); f[x][3]=min(f[x][2],f[x][3]); f[x][4]=min(f[x][3],f[x][4]); // int k=node[x],i,j,y; // while(k!=0) // { // y=edge[k].to; // dfs(y); // for(i=2;i<=4;i++) // for(j=0;j<i;j++) // f[y][i]=min(f[y][i],f[y][j]); // f[x][0]+=f[y][4]; // f[x][3]+=f[y][2]; // f[x][4]+=f[y][3]; // k=edge[k].next; // } // f[x][0]++; // k=node[x]; // f[x][1]=0x3f3f3f3f;f[x][2]=0x3f3f3f3f; // while(k!=0) // { // y=edge[k].to; // f[x][1]=min(f[x][1],f[y][0]-f[y][3]); // f[x][2]=min(f[x][2],f[y][1]-f[y][2]); // k=edge[k].next; // } // f[x][1]+=f[x][4]; // f[x][2]+=f[x][3]; } int main() { int i,t; scanf("%d",&n); for(i=2;i<=n;i++) { scanf("%d",&t); make(t,i); } dfs(1); printf("%d",f[1][2]); //此时f[1][2]已经是min(f[1][0..2])了 return 0; }