【NOIP2017提高A组冲刺11.6】拆网线

这篇博客探讨了在企鹅国的网吧网络中,如何在满足K只企鹅至少能与其他企鹅联机游戏的前提下,拆除最少的网线。文章通过分析不同比例的数据约束,提出了从暴力搜索到动态规划的解决方案,其中100%数据做法结合了贪心思想,计算每个子树的最大两两匹配数来确定最少保留的网线数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题面

Description

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

Input

第一行一个整数T,表示数据组数;
每组数据第一行两个整数N,K,表示总共的机房数目和企鹅数目。
第二行N-1个整数,第i个整数Ai表示机房i+1和机房Ai有一根网线连接(1≤Ai≤i)。

Output

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

Sample Input

2
4 4
1 2 3
4 3
1 1 1

Sample Output

2
2

Data Constraint

对于30%的数据:N≤15;
对于50%的数据:N≤300;
对于70%的数据:N≤2000;
对于100%的数据:2≤K≤N≤100000,T≤10。

思路

30 % 30\% 30%数据做法

暴力搜索枚举删那些边,验证即可。

50 % 50\% 50%数据做法

f i , j , k f_{i,j,k} fi,j,k表示当前子树根节点为 i i i,与子树中 j j j个结点相连,是否安排了 k k k只企鹅。时间复杂度 O ( n 3 ) O(n^3) O(n3),预计得分: 50 50 50

70 % 70\% 70%数据做法

考虑 D P DP DP
f i , j f_{i,j} fi,j表示子树根节点 i i i与子树中 j j j个节点相连,最多安排了多少企鹅。时间复杂度 O ( n 2 ) O(n^2) O(n2),预计得分:70。

100 % 100\% 100%数据做法

仍旧是考虑 D P DP DP,掺杂了一些贪心的思想。
一条边可以连接两个企鹅站,那么这样的一对点对越多越好。
如果有 p p p对点对,那么我们有:
p ≥ k p\ge k pk,我们只需要 ⌈ k 2 ⌉ \lceil \frac k2\rceil 2k条边 ;
否则,我们需要 p 2 + ( k − p ) \frac p2+(k-p) 2p+(kp)条边。
那么问题就转换为求上述的 p p p了。
f i , 0 f_{i,0} fi,0表示以 i i i为根的子树最大两两匹配数,不包含 i i i节点
    f i , 1 ~~~f_{i,1}    fi,1表示以 i i i为根的子树最大两两匹配数,包含 i i i节点
那么我们有转移方程:
f i , 0 = ∑ s o n f s o n , 1 f_{i,0}=\sum_{son}f_{son,1} fi,0=sonfson,1
f i , 1 = m a x ( f i , 1 , f i , 0 − f s o n , 1 + f s o n , 0 + 2 ) f_{i,1}=max(f_{i,1},f_{i,0}-f_{son,1}+f_{son,0}+2) fi,1=max(fi,1,fi,0fson,1+fson,0+2)

Code

#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
int T,cnt,head[200005],next[200005],road[200005],f[100005][2];
int max(int a,int b){return a>b?a:b;}
void add(int u,int v)
{
	next[++cnt]=head[u];
	head[u]=cnt;
	road[cnt]=v;
}
void dfs(int u,int from)
{
	for(int i=head[u];i;i=next[i])
	{
		int v=road[i];
		if(v==from) continue;
		dfs(v,u);
		f[u][0]+=f[v][1];
	}
	for(int i=head[u];i;i=next[i])
	{
		int v=road[i];
		if(v==from) continue;
		f[u][1]=max(f[u][1],f[u][0]-f[v][1]+f[v][0]+2);
	}
}
int main()
{
	freopen("tree.in","r",stdin);
	freopen("tree.out","w",stdout);
	scanf("%d",&T);
	while(T--)
	{
		int n,k,p;
		memset(f,0,sizeof(f));
		memset(head,0,sizeof(head));
		memset(next,0,sizeof(next));
		memset(road,0,sizeof(road));
		cnt=0;
		scanf("%d%d",&n,&k);
		for(int i=1;i<n;i++)
		{
			int u;
			scanf("%d",&u);
			add(u,i+1),add(i+1,u);
		}
		dfs(1,0);
		p=max(f[1][0],f[1][1]);
		if(p>=k) printf("%d\n",(k+1)/2);
		else printf("%d\n",p/2+(k-p));
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值