刷题总结——拆网线(noip模拟 贪心)

题目:

  给定一颗树··在保证有k个点与其它点连接的情况下问最少保留多少条边····

  树的节点树n和k均小于100000;

题解:

  很容易看出来我们要尽量保留那种一条边连两个节点的情况····

  然后考试的时候我以为这就完了··xjb贪完心后错了一大半····

  下次一定要写对拍了,艹

  贪心的时候我们要沿着叶子节点来贪心···这样就能保证正确性了···证明的话就不细说了··不信的话打个对拍看看···

代码:

  

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<ctime>
#include<cctype>
#include<string>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1e5+5;
int T,n,K,fst[N],nxt[N*2],go[N*2],tot,cnt,keep;
bool del[N];
inline int R()
{
  char c;int f=0;
  for(c=getchar();c<'0'||c>'9';c=getchar());
  for(;c<='9'&&c>='0';c=getchar())  f=(f<<3)+(f<<1)+c-'0';
  return f;
}
inline void comb(int a,int b)
{
  nxt[++tot]=fst[a],fst[a]=tot,go[tot]=b;
  nxt[++tot]=fst[b],fst[b]=tot,go[tot]=a;
}
inline void pre()
{
  cnt=tot=keep=0;
  memset(fst,0,sizeof(fst));
  memset(del,false,sizeof(del));
}
inline void dfs(int u,int fa)
{
  for(int e=fst[u];e;e=nxt[e])
  {
    int v=go[e];if(v==fa)  continue;
    dfs(v,u);
    if(!del[v]&&!del[u])
    {
      if(keep<K)
      {
        keep+=2;cnt++;
        del[v]=del[u]=true;
      }
    }
  }
}
int main()
{
  //freopen("a.in","r",stdin);
  T=R();int a;  
  while(T--)  
  {
    n=R(),K=R();pre();
    for(int i=1;i<n;i++)  a=R(),comb(i+1,a);
    dfs(1,0);
    if(keep>=K)  cout<<cnt<<"\n";
    else cout<<cnt+(K-keep)<<"\n";
  }
  return 0;
}

 

posted @ 2017-10-23 20:03  AseanA  阅读(369)  评论(0编辑  收藏  举报