算法竞赛——进阶指南——acwng350. 巡逻 树的直径性质,(证明为什么K==2总有一条链选择直径)

先写下证明:(相信有人跟我一样不理解为什么K==2时第一条链必须选择直径,不能选两条不相交的次长链由于直径+相交的次长链嘛)看图:

直径A-B

先考虑如果不选直径,选两条次长链:

A-2   B-3   (显然不选1-2,否则1-B就是直径,点4同理不选,而且为了更优,肯定选择不相交的两条,如果相交不如选择直径加其中一条长链)

长度为: AD+D2+3E+EB;

而选择直径+次长链:的长度为AD+DE+EB+3E+D2-DE与前者长度刚好相同

(如果DE过长即3D+D2<DE则我们直接选取max(3E,D2),长度为:AD+DE+EB+max(3E+D2),也是不小于第一种情况的。)

发现:最优情况前者也只是等于后者,所以选直径+次长链是最优情况。、

综上:K==2时,先选择直径,再选则一条长链,是优解。

然后思路就简单了:、

先选择直径,然后把直径上的边都赋值成-1,表示选择边就会使结果变差1.

再在这个新的边权树上选择最长链。

结果就是:2*(n-1)-(L1-1)-(L2-1)

注意直径不能用两次dfs//因为有负边权,第一次dfs选的不一定是直径的端点,反例很好找。

然后就是一些细节问题,具体见代码!

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define ls (o<<1)
#define rs (o<<1|1)
#define pb push_back
const double PI= acos(-1.0);
const int M = 1e5+7;

int head[M],cnt;
void init(){cnt=0,memset(head,0,sizeof(head));}
struct EDGE{int to,nxt,w;}ee[M*2];
void add(int x,int y,int w){ee[++cnt].nxt=head[x],ee[cnt].w=w,ee[cnt].to=y,head[x]=cnt;}

int vs[M],f[M],dp[M];
int p,q,mx=-1;
void dfs(int x,int fa,int dep)
{
	if(mx<dep)mx=dep,p=x;
	for(int i=head[x];i;i=ee[i].nxt)
	{
		int y=ee[i].to;
		if(y==fa)continue;
		if(q!=0)f[y]=x;
		dfs(y,x,dep+1);
	}
}
int ans;
void dfs2(int x,int fa)
{
	for(int i=head[x];i;i=ee[i].nxt)
	{
		int y=ee[i].to,w=1;
		if(y==fa)continue;
		if(vs[x]&&vs[y])w=-1;
		dfs2(y,x);
		ans=max(ans,dp[x]+dp[y]+w);
		dp[x]=max(dp[y]+w,dp[x]);
	}
}
int main()
{
	ios::sync_with_stdio(false);
  	cin.tie(0);
  	int n,k;
	  cin>>n>>k;
	for(int i=1;i<n;i++)
	{
		int u,v;
		cin>>u>>v;
		add(u,v,1);
		add(v,u,1);
	} 
	mx=0;dfs(1,0,0);q=p;
	mx=0;dfs(p,0,0);
	//cout<<p<<" "<<q<<endl;
	if(k==1)
	{
		cout<<2*(n-1)-mx+1<<endl;
		return 0;
	}
	while(p!=0)
	{
		vs[p]=1;
		p=f[p];
	}
	dfs2(1,0);
	mx+=ans;
	cout<<2*(n-1)-mx+2<<endl;
	return 0;
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值