HDU-6105 Gameia - 2017 Multi-University Training Contest - Team 6(思维之找规律或二分图最大匹配)

本文解析了一款基于树形结构的两人博弈游戏策略。通过分析树的性质和操作规则,提出了判断先手玩家获胜条件的方法,包括奇数顶点情况下的必胜策略、偶数顶点情况下的复杂判断及2-连通分支切割策略。

题意:

一棵1为根n个节点的树,Alice先手,Bob后手, Alice的操作是选一个未染色的点将其染成白色,Bob的操作是选一个未染色的点将其染成黑色,并且和这个点直接连边的点也被强制染成黑色(无论这些点之前是否有颜色),Bob还可以任意时刻切断k条边,当所有点都有颜色的时候,游戏结束,如果存在白色点Alice赢,否则Bob赢。

思路:

就暂且把所有的先手赢还是输都当作博弈吧...其实本题就是一个思维题,首先如果有奇数个顶点,Bob必输,画一下就知道了(每次都选择"叶子"节点的父亲节点染白色,Bob只能染该叶子节点,最终轮到Alice还会剩一个点)。当为偶数时,我是以所有顶点为根进行n次树的遍历,如果该根节点有>=2个奇数子树(不算该根节点),Bob必输(仍然是上述思路将两个奇数长度子树不断染色直至该子树剩余一个点,则造成Alice必胜),再否则就得判断Bob能否将该树切成若干个2-连通分支(就是死在这儿...只要能想到2-连通分支这点很容易推出k如果大于等于n/2-1,则能成功切割。唉,智障思维卡住愣是没想到),如果能则Bob必胜,否则Alice必胜。


代码:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 505;
struct node
{
	int v, next;
} edge[maxn*2];
int no, head[maxn];
int n, k;
inline void init()
{
	no = 0;
	memset(head, -1, sizeof head);
}
inline void add(int u, int v)
{
	edge[no].v = v;
	edge[no].next = head[u];
	head[u] = no++;
}
int dfs(int cur, int father)
{
	int sum = 1;
	for(int k = head[cur]; k != -1; k = edge[k].next)
	{
		int v = edge[k].v;
		if(v == father) continue;
		sum += dfs(v, cur);
	}
	return sum;
}
int jg(int cur)
{
	int now = 0;
	for(int k = head[cur]; k != -1; k = edge[k].next)
	{
		int v = edge[k].v;
		int t = dfs(v, cur);
		if(t&1) ++now;
		if(now >= 2) return -1;
	}
	return 1;
}
int main()
{
	//freopen("in.txt", "r", stdin);
	int t, x, cnt;
	scanf("%d", &t);
	while(t--)
	{
		scanf("%d %d", &n, &k); init();
		for(int i = 2; i <= n; ++i)
		{
			scanf("%d", &x);
			add(x, i), add(i, x);
		}
		if(n&1) {puts("Alice"); continue;}
		cnt = 0;
		for(int i = 1; i <= n; ++i)
		{	
			cnt = jg(i);
			if(cnt == -1) break;
		}
		if(cnt == -1) puts("Alice");
		else if(k-n/2+1 >= 0) puts("Bob");
		else puts("Alice");
	}
	return 0;
}


其实问题仔细分析发现就是判可不可以将所有点分成若干个2-连通分支,所以就变成求二分图的最大匹配了,然后再判断k是否能够分割得了。


代码:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 505;
vector<int> G[maxn];
int vis[maxn], match[maxn];
int n, k;
int dfs(int u)
{
	for(int i = 0; i < G[u].size(); ++i)
	{
		int v = G[u][i];
		if(vis[v]) continue;
		vis[v] = 1;
		if(match[v] == -1 || dfs(match[v]))
		{
			match[v] = u;
			return 1;
		}
	}
	return 0;
}
int main()
{
	int t, u, ans;
	scanf("%d", &t);
	while(t--)
	{
		scanf("%d %d", &n, &k);
		for(int i = 1; i <= n; ++i) G[i].clear();
		memset(match, -1, sizeof match);
		for(int i = 2; i <= n; ++i)
		{
			scanf("%d", &u);
			G[i].push_back(u);
			G[u].push_back(i);
		}
		ans = 0;
		for(int i = 1; i <= n; ++i)
		{
			memset(vis, 0, sizeof vis);
			if(dfs(i)) ++ans;
		}
		if(ans == n && k >= n/2-1) puts("Bob");
		else puts("Alice");
	}
	return 0;
}


比完赛看题解恍然大悟的感觉贼鸡儿不爽,好多次都只差一点,很烦,继续加油~

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值