哈密尔顿生成树,51nod1767,树形Dp+结论猜测

本文探讨了在树形结构中寻找最小路径覆盖的问题,针对x大于等于y和x小于y两种情况,分别提出了不同的解决方案。对于x大于等于y的情况,文章详细分析了如何在不同类型的树中找到至少包含一条树边的路径;而对于x小于y的情况,则将其转化为最小树上路径覆盖问题,并介绍了如何使用动态规划的方法进行求解。

正题

      Portal     

      首先考虑x>=y的情况,先说结论:

      当给出的树是一个菊花图时,就必须走一条树边,否则可以构造出一条路径使得其不经过树边。

      菊花图的时候,如果从根开始走,那么只能通过一条树边来到达其他点,如果从叶子开始走,要走到根必须走一条树边。

      若不是菊花图,则可以不经过树边。

      首先考虑一个层数>=4的一张图。

      每一层缩成一个点。(因为到达这一层的任意一个点就可以到达这一层的另外一些点)

      那么就变成一条长>=4的一条链,从上往下选\left \lceil \frac{n}{2} \right \rceil这个点为起始点,然后依次经过n,1,n-1,2,...,若n为奇数,那么结尾就在起始点旁边,若n为偶数,那么结尾就为\frac{n}{2}+1这个点。在走的过程中,每一步都跳到了不与自己相邻的一个点。不太懂的可以画图验证。

      讨论一个层数=3的树,若根的儿子=1,那么可以转化为一棵层数为2的树(菊花图)

      否则可以转化为一棵层数为4的树,取一个叶子当根即可。

      那么我们就证明了这个定理,即使不是那么的严谨。

      接着讨论x<y的情况。

      那么相当于求最小树上路径覆盖。

      这个东西在DAG上可以直接使用网络流来求解,在无向图上比较困难。

      树这种无向图有什么特殊情况呢?

      我们直接设f[x][0/1]表示覆盖完x这个子树,向上不延伸/延伸一条路径的最小路径覆盖。

      然后转移具体可以看一下代码,在这里就不说了,还是比较好想的。

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
using namespace std;

const int N=200010;
int n,a,b;
struct edge{
	int y,next;
}s[N<<1];
int in[N];
int f[N][2],first[N],len;

void ins(int x,int y){
	s[++len]=(edge){y,first[x]};first[x]=len;in[x]++;
	s[++len]=(edge){x,first[y]};first[y]=len;in[y]++;
}

void dfs(int x,int fa){
	int mmin=1e9,cmin=1e9;
	for(int i=first[x];i!=0;i=s[i].next) if(s[i].y!=fa){
		int y=s[i].y;
		dfs(y,x);
		f[x][0]+=f[y][0];
		if(f[y][1]-f[y][0]<mmin) cmin=mmin,mmin=f[y][1]-f[y][0]; 
		else if(f[y][1]-f[y][0]<cmin) cmin=f[y][1]-f[y][0];
		f[x][1]+=f[y][0];
	}
	if(mmin==1e9) {f[x][0]=f[x][1]=1;return ;}
	f[x][1]+=min(1,mmin);
	if(cmin==1e9) {f[x][0]=f[x][1];return ;}
	f[x][0]+=min(1,mmin+cmin-1);
}

int main(){
	scanf("%d %d %d",&n,&a,&b);
	int x,y;
	for(int i=1;i<n;i++) scanf("%d %d",&x,&y),ins(x,y);
	if(a>=b){
		int mmax=0;
		for(int i=1;i<=n;i++) mmax=max(mmax,in[i]);
		if(mmax==n-1) printf("%lld",1ll*b*(n-2)+a);
		else printf("%lld",1ll*b*(n-1));
		return 0;
	}
	dfs(1,0);
	printf("%lld",1ll*(f[1][0]-1)*b+1ll*(n-f[1][0])*a);
}

 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值