【AtCoder】【思维分析】Shorten Diameter(AGC001)

11 篇文章 0 订阅
6 篇文章 0 订阅

题意:

从一个含有n个点的树里面,要求你删除最少的点,满足剩下来的树的直径小于等于K。要求最终的图仍然是联通的。

数据范围:

树的节点数:1<=n<=2000。

思路:

最开始的时候,也就是在考试的时候,我想到的并不是正解,但是居然骗到了ACヾ(◍°∇°◍)ノ゙!!!
大概是这样的:每次从中取出一条直径(知道两个端点就好了),然后比较两个点的“影响力”。所谓影响力,因为我们知道一棵树当中可能含有多条直径,而这里的影响力就是指的以这个点为edpoints的直径的条数。然后删去“影响力”大的那个点(因为直径的两个端点总是叶子)。这样子每次都算一次直径是O(n),然后算O(n)次,就是O(n^2)的,看起来很完美,但是是错误的…可能有多个原因,但我知道其中的一个可能是当两个点的影响力相同时,我是随机选取的其中一个端点,然而可能其中的一个节点对于那些“非直径”的路径的影响力更大,然而我选择了另外一条,然后就错了。
下面进入正题:
其实需要进行K的分类讨论。我们发现,如果一棵树的直径小于等于K,那么等价于:1.当K为奇数时,枚举一条边,然后把这条边的两个端点都放在深度0,然后 d e p m a x &lt; = ⌊ K 2 ⌋ dep_{max}&lt;=\lfloor\dfrac{K}{2}\rfloor depmax<=2K即可(可以fa个图自己感受一下啦);2.当K位偶数时,枚举一个点,将这个点放在深度0,然后同样满足 d e p m a x &lt; = ⌊ K 2 ⌋ dep_{max}&lt;=\lfloor\dfrac{K}{2}\rfloor depmax<=2K就可以了。(感觉自己在考场上想复杂了呢…)。
这样就是枚举一条边/点,是O(n)的;遍历整棵树,是O(n)的。整体就是O(n^2)的,得到解决。

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define MAXN 2000
using namespace std;
vector<int> G[MAXN+5];
int N,K,tot=0;
void DFS(int u,int fa,int depn)
{
	if(depn>K)
		return;
	tot++;//可以计算深度小于等于K/2的,然后用总数来减,比较好想。
	for(int i=0;i<(int)G[u].size();i++)
	{
		int v=G[u][i];
		if(v==fa)
			continue;
		DFS(v,u,depn+1);
	}
}
int main()
{
	scanf("%d %d",&N,&K);
	int u,v,typ;
	for(int i=1;i<N;i++)
	{
		scanf("%d %d",&u,&v);
		G[u].push_back(v);
		G[v].push_back(u);
	}
	if(K%2==0)	typ=1;
	else		typ=0;
	int ans=N;
	K/=2;
	if(typ==1)//分情况讨论
	{
		for(int i=1;i<=N;i++)
		{
			tot=0;
			DFS(i,-1,0);//把一个点放在深度0.
			ans=min(ans,N-tot);
		}
	}
	else if(typ==0)
	{
		for(u=1;u<=N;u++)
			for(int j=0;j<(int)G[u].size();j++)
			{
				v=G[u][j];
				tot=0;
				DFS(v,u,0);//把一条边的两个点都放在深度0.
				DFS(u,v,0);
				ans=min(ans,N-tot);
			}
	}
	printf("%d\n",ans);
	return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值