不充钱,你怎么AC?
题目:http://codevs.cn/problem/1091/
大家都写的 DFS,然而我想到了一种贪心的做法,重点是可以A
普遍的贪心是每次删掉该深度子树最大的点,但是如果有一边卡一条链就会WA
我们何不进一步考虑贪心,如果它下面是一条链我们就可以缓一缓到第 2 天再删是不是,反正它每次也就增加一个人
用 size[x] 记录所有后代加上自己的节点个数,f[x] 记录其最大子树的 size 值
我们考虑第一次不删这个节点,让它先扩展一次,然后第二次再删除它子树中 size 最大的节点
那么设 g[x]=size[x]-f[x],g 就为第一次不删,第二次删掉其最大的子树还剩余的节点数
一条链的 g[x]=1,而一个多叉节点的 g[x]>1,这就意味着先要删去 g 值更大的那个节点,才能不让它下一次扩展更多的出来
那么一层层地贪心,每次删掉 g 值最大的节点,直到不再继续传染为止
贪心跑得飞快~
1 #include<algorithm> 2 #include<iostream> 3 #include<cstdlib> 4 #include<cstring> 5 #include<cstdio> 6 #include<cmath> 7 #include<stack> 8 using namespace std; 9 10 const int N=301; 11 stack<int> s,q; 12 int first[N],v[N*2],next[N*2],size[N],g[N],f[N]; 13 void dfs(int x,int fa) 14 { 15 f[x]=fa; 16 int i; 17 for (i=first[x];i;i=next[i]) 18 { 19 if (v[i]==fa) continue; 20 dfs(v[i],x); 21 if (size[v[i]]>g[x]) g[x]=size[v[i]]; 22 } 23 size[fa]+=size[x]; 24 } 25 int main() 26 { 27 int n,m,i,x,ans=0; 28 scanf("%d%d",&n,&m); 29 for (i=1;i<=m;i++) 30 { 31 scanf("%d%d",&x,&v[i]); 32 v[i+m]=x; 33 next[i]=first[x]; 34 first[x]=i; 35 next[i+m]=first[v[i]]; 36 first[v[i]]=i+m; 37 } 38 for (i=1;i<=n;i++) size[i]=1; 39 dfs(1,0); 40 for (i=1;i<=n;i++) g[i]=size[i]-g[i]; 41 s.push(1); 42 g[0]=size[0]=0; 43 ans=n; 44 while (!s.empty()) 45 { 46 m=0; 47 while (!s.empty()) 48 { 49 x=s.top(); 50 s.pop(); 51 for (i=first[x];i;i=next[i]) 52 { 53 if (v[i]==f[x]) continue; 54 if (g[v[i]]>g[m]||(g[v[i]]==g[m]&&size[v[i]]>size[m])) m=v[i]; 55 q.push(v[i]); 56 } 57 } 58 while (!q.empty()) 59 { 60 x=q.top(); 61 q.pop(); 62 if (x==m) ans-=size[m]; 63 else s.push(x); 64 } 65 } 66 printf("%d ",ans); 67 return 0; 68 }
这里有个DFS的:http://blog.csdn.net/yuyanggo/article/details/48087431
贪心的做法证明有人会严谨的吗?会的话告诉我谢谢!