Burza题解(贪心分析+状压Dp)

8 篇文章 0 订阅
7 篇文章 0 订阅

题目大意

给你一棵有 n n n 个节点的树,现在有一枚硬币在1号节点上。有两名玩家A、B,按照以下顺序操作:
a. A选择一个节点并标记它
b. B将硬币移动到一个与硬币当前所在的节点相邻的未被标记的节点
c.硬币原来所在节点被标记
A玩家只知道硬币一开始在在1号节点上,但他希望无论B如何操作硬币最多移动k次,问是否能完成。(输出‘DA’或‘NE’)
数据范围: 1 ≤ k ≤ n ≤ 400 1≤k≤n≤400 1kn400

分析

这道题有许多人想了各种贪心策略,但是出题人都卡了……
首先我们可以记节点深度为 d e p ( d e p [ 1 ] = 0 ) dep (dep[1]=0) dep(dep[1]=0)
那么对于 d e p [ u ] > k dep[u]>k dep[u]>k 的节点我们其实是可以砍了的,因为硬币最多只会向下移动k次
然后假设现在是第i次操作,那么我们在 d e p = i dep=i dep=i 中选一个堵住是合理的,当 d e p &lt; i dep&lt;i dep<i时我们是堵不住的(我们并不知道硬币怎么走的);当 d e p &gt; i dep&gt;i dep>i 时,显然应该堵 d e p = i dep=i dep=i(可能在分岔口堵更多)

对于A而言,某一位置的硬币所在子树中图2是优于图1的,
在这里插入图片描述
可以理解为本来在分岔口堵一个变为还要在i+1堵一次。
为了方便描述,我们记 d e p = k dep=k dep=k 的节点为 l e a f leaf leaf, c n t [ u ] cnt[u] cnt[u]表示子树内叶节点个数
那么我们发现,我们堵 c n t [ u ] cnt[u] cnt[u] 更大的节点是利于A获胜的,所以我们要构造一棵树来使整体的 c n t cnt cnt都很小
构造的图如下:
在这里插入图片描述
那么我们发现叶节点总数为 ⌊ n / k ⌋ \lfloor n/k\rfloor n/k,我们可以堵的次数是k,而当floor(n/k) ≤k时我们就可以堵完,一次堵一个,我们想得到 n ≤ k ∗ k n≤k*k nkk,发现变形有点问题。那我们假设 n ≤ k n≤k nk 我们可以得到
⌊ n / k ⌋ \lfloor n/k \rfloor n/k ≤n≤k。即当 n ≤ k ∗ k n≤k*k nkk时是存在必胜策略的。
(这里我和题解有些出入,有兴趣的同学可以再看看题解证明)
那么我们只需要处理 k 2 &lt; n k^2&lt;n k2<n 的情况了
我们发现 n ≤ 400 n≤400 n400,那么 k ≤ 19 k≤19 k19 ,我们就可以考虑一下Dp了。
我们将叶节点从左至右编号 l e a f [ i ] leaf[i] leaf[i],总数为tot。
我们定义f[i][S]:是否能用深度集合为S的节点将前i个叶节点被堵住。
注意,这里深度集合指的是S的第i位表示dep=i是否有节点被堵住
(因为同一深度节点只能被堵一次)
我们还可以用两个数组L[u],R[u]来记录u字数内包含的叶节点区间
( l e a f [ L [ u ] ] , l e a f [ R [ u ] ] ] (leaf[L[u]],leaf[R[u]]] (leaf[L[u]],leaf[R[u]]].
我们再用 H [ i ] [ t ] H[i][t] H[i][t] 记录 R [ u ] R[u] R[u] 均为i的节点编号u,便于转移
那么转台转移方程就是:
f [ i ] [ S ] ∣ = f [ L [ H [ i ] [ t ] ] ] [ S − ( 1 &lt; &lt; ( d e p [ H [ i ] [ t ] ] − 1 ) ) ] f[i][S]|=f[L[H[i][t]]][S-(1&lt;&lt;(dep[H[i][t]]-1))] f[i][S]=f[L[H[i][t]]][S(1<<(dep[H[i][t]]1))]
初始化 f [ 0 ] [ 0 ] = 1 f[0][0]=1 f[0][0]=1
可以具体看实现
最后判断 f [ t o t ] [ j ] ( 1 &lt; = j &lt; ( 1 &lt; &lt; k ) ) f[tot][j](1&lt;=j&lt;(1&lt;&lt;k)) f[tot][j](1<=j<(1<<k)) 即可。

代码

#include<set>
#include<map>
#include<stack>
#include<cmath>
#include<queue>
#include<deque>
#include<cstdio>
#include<bitset>
#include<vector>
#include<climits>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define LL long long
int read(){
	int f=1,x=0;char c=getchar();
	while(c<'0'||'9'<c){if(c=='-')f=-1;c=getchar();}
	while('0'<=c&&c<='9') x=(x<<3)+(x<<1)+c-'0',c=getchar();
	return f*x;
}
#define MAXK 20
#define MAXN 400
#define INF 0x3f3f3f3f
vector<int> G[MAXN+5];
inline void Addedge(int u,int v){
	G[u].push_back(v),G[v].push_back(u);
	return ;
}
int n,k,cnt,fa[MAXN+5],dep[MAXN+5],L[MAXN+5],R[MAXN+5];
void DFS(int u){
	dep[u]=dep[fa[u]]+1;
	if(dep[u]==k){
		L[u]=cnt++,R[u]=cnt;
		return ;
	}
	L[u]=cnt;
	for(int i=0;i<int(G[u].size());i++){
		int v=G[u][i];
		if(v==fa[u]) continue;
		fa[v]=u,DFS(v);
	}
	R[u]=cnt;
	return ;
}
vector<int> H[MAXN+5];
bool f[MAXN+5][1<<MAXK];
bool Dp(){//f[i][S]:前i个叶子用深度点集为S是否能阻挡
	f[0][0]=1;
	for(int i=1;i<=n;i++)
		if(dep[i])
			H[R[i]].push_back(i);
	for(int i=1;i<=cnt;i++)
		for(int t=0;t<int(H[i].size());t++)
			for(int S=0;S<(1<<k);S++)
				if(S&(1<<(dep[H[i][t]]-1)))
					f[i][S]|=f[L[H[i][t]]][S^(1<<(dep[H[i][t]]-1))];
	for(int S=0;S<(1<<k);S++)
		if(f[cnt][S])
			return 1;
	return 0;
}
int main(){
	//freopen("burza.in","r",stdin);
	//freopen("burza.out","w",stdout);
	n=read(),k=read();
	if(n<=k*k){
		puts("DA");
		return 0;
	}
	for(int i=1;i<n;i++){
		int u=read(),v=read();
		Addedge(u,v);
	}
	dep[0]=-1;
	DFS(1);
	if(Dp()) puts("DA");
	else puts("NE");
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值