[Contest1351]拆网线

题面

Description

【问题描述】

企鹅国的网吧们之间由网线互相连接,形成一棵树的结构。现在由于冬天到了,供暖部门缺少燃料,于是他们决定去拆一些网线来做燃料。但是现在有K只企鹅要上网和别人联机游戏,所以他们需要把这K只企鹅安排到不同的机房(两只企鹅在同一个机房会吵架),然后拆掉一些网线,但是需要保证每只企鹅至少还能通过留下来的网线和至少另一只企鹅联机游戏。

所以他们想知道,最少需要保留多少根网线?

 

【输入格式】

第一行一个整数T,表示数据组数;

每组数据第一行两个整数N,K,表示总共的机房数目和企鹅数目。

第二行N-1个整数,第i个整数Ai表示机房i+1和机房Ai有一根网线连接(1≤Ai≤i)。

 

【输出格式】

每组数据输出一个整数表示最少保留的网线数目。

 

【输入样例】

2

4 4

1 2 3

4 3

1 1 1

 

【输出样例】

2

2

 

【数据范围】

对于30%的数据:N≤15;

对于50%的数据:N≤300;

对于70%的数据:N≤2000;

对于100%的数据:2≤K≤N≤100000,T≤10。

题意

有一颗树,要求删最多的边,但要使得每个点必须至少与一个点相连。问最少剩几条边。

题解

按拓扑序贪心,正确性我也不会证

#include<iostream>
#include<cstring>
using namespace std;
const int N=2e5+5;
struct edge{
    int v,next;
}e[N<<1];
int t,n,k,head[N],cnt,clo,rk[N],ans;
bool vis[N];
void add(int u,int v){
    e[++cnt]=(edge){v,head[u]};
    head[u]=cnt;
}
void dfs(int u,int fa){
    for(int i=head[u];i;i=e[i].next){
        int v=e[i].v;
        if(v==fa)continue;
        dfs(v,u);
    }
    rk[++clo]=u;
}
int main(){
    scanf("%d",&t);
    while(t--){
        ans=cnt=clo=0;
        memset(vis,0,sizeof(vis));
        memset(head,0,sizeof(head));
        scanf("%d%d",&n,&k);
        for(int i=1;i<n;i++){
            int x;
            scanf("%d",&x);
            add(i+1,x);
            add(x,i+1);
        }
        dfs(1,0);
        for(int i=1;i<=n;i++){
            if(k<=1)break;
            int u=rk[i];
            if(vis[u])continue;
            for(int j=head[u];j;j=e[j].next){
                int v=e[j].v;
                if(!vis[v]){
                    vis[v]=1;
                    ans++;
                    k-=2;
                    break;
                }
            }
            vis[u]=1;
        }
        ans+=k;
        printf("%d\n",ans);
    }
}

  

posted @ 2019-08-26 14:53  Evan704  阅读(263)  评论(0编辑  收藏  举报