题目大意:
给你一个n个结点的树,请你搞一些破坏。
你可以选择手动弄坏某个点,那么与它直接相连的点也会自动坏掉。
问你把整棵树搞坏至少要手动弄坏几个点?
思路:
f[0~2][i]表示不同状态下以i为根的子树至少要手动弄坏几个点。
我们可以把点的不同状态分为以下三种:
0:它的孩子有被手动弄坏的。
1:它自己被手动弄坏。
2:它父母被手动弄坏。
设当前根为x,枚举每个孩子y,一个固定的手动弄坏的孩子z,转移方程如下:
f[0][x]=sum{min(f[0][y],f[1][y])|y≠z}+f[1][z];
f[1][x]=sum{min(f[0][y],f[1][y],f[2][y])}+1;
f[2][x]=sum{min(f[0][y],f[1][y])};
最后答案为min(f[0][1],f[1][1])。
1 #include<cstdio> 2 #include<cctype> 3 #include<vector> 4 inline int getint() { 5 register char ch; 6 while(!isdigit(ch=getchar())); 7 register int x=ch^'0'; 8 while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0'); 9 return x; 10 } 11 const int inf=0x7fffffff; 12 const int N=10001; 13 std::vector<int> e[N]; 14 inline void add_edge(const int &u,const int &v) { 15 e[u].push_back(v); 16 e[v].push_back(u); 17 } 18 int f[3][N]; 19 int t[N]; 20 void dp(const int &x,const int &par) { 21 f[0][x]=inf; 22 f[1][x]=1; 23 int tmp=0; 24 for(register std::vector<int>::iterator i=e[x].begin();i!=e[x].end();i++) { 25 const int &y=*i; 26 if(y==par) continue; 27 dp(y,x); 28 t[y]=tmp; 29 tmp+=std::min(f[0][y],f[1][y]); 30 f[1][x]+=std::min(std::min(f[0][y],f[1][y]),f[2][y]); 31 f[2][x]+=std::min(f[0][y],f[1][y]); 32 } 33 tmp=0; 34 for(register std::vector<int>::reverse_iterator i=e[x].rbegin();i!=e[x].rend();i++) { 35 const int &y=*i; 36 if(y==par) continue; 37 t[y]+=tmp; 38 tmp+=std::min(f[0][y],f[1][y]); 39 } 40 for(register std::vector<int>::iterator i=e[x].begin();i!=e[x].end();i++) { 41 const int &y=*i; 42 if(y==par) continue; 43 f[0][x]=std::min(f[0][x],t[y]+f[1][y]); 44 } 45 } 46 int main() { 47 const int n=getint(); 48 for(register int i=1;i<n;i++) { 49 add_edge(getint(),getint()); 50 } 51 dp(1,0); 52 printf("%d ",std::min(f[0][1],f[1][1])); 53 return 0; 54 }