STEVE - Voracious Steve dfs , 以及为什么不能博弈

文章讲述了在SPOJ网站的问题中,解决关于两个玩家轮流拿甜甜圈的博弈问题,通过贪心策略和深度优先搜索(DFS)分析最优解,探讨了如何利用记忆化来避免重复计算。关键在于理解何时必败并最大化自己的收益。
摘要由CSDN通过智能技术生成

SPOJ.com - Problem STEVE

STEVE - Voracious Steve - 洛谷

题意:

n个甜甜圈,两个人,每次没人最多拿m个。谁先拿完谁吃掉所有当前拿的,另一个人把手上的放回,再来第二轮...

思路:

为什么不能博弈:

博弈思路:

//0~m 直接吃
//让对手是m+1,对手不管选什么,都是我们吃
//m+2~2m+1 都可以让cur为m+1,在这个范围内的可以赢
//2m+2 败
//2m+3~3m+2 胜
//3m+3 败
// 
//km+k必败 -> 必败了,那我就尽可能多拿,让你少拿

//				cur
// km+k
//				(k-1)m+k
// (k-1)m+k-1
//				(k-2)m+k-1
//
//	m+1
//					1
//
//				  **k**

//下一轮 km

// m == 1
//			5
// 2+2
//			2+1
// 1+1
//			1
//			3
//

// 2
// 1+1
//			1
//			1

// 1

void solve()
{
	cin >> n >> m;
	int cur = n;
	int ans = 0;
	int k = 0;
	while (1)
	{
		while (1)
		{
			k = cur / m;
			if (cur == k * m + k)
			{
				cur = k * m;
			}
			else
			{
				while (cur - k * m < k)
				{
					k--;
				}
				if (cur == k * m + k)
				{
					cur = k * m;
					continue;
				}
				else if (cur - k * m > k)
				{
					ans += cur - (k * m + k);
				}
				if (k <= 0)break;
				ans += k;
				cur = k * m;
			}
			break;///
		}
		if (k <= 0)break;
		
		while (1)
		{
			k = cur / m;
			if (cur == k * m + k)
			{
				cur = k * m;
			}
			else
			{
				while (cur - k * m < k)
				{
					k--;
				}
				if (cur == k * m + k)
				{
					cur = k * m;
					ans += k;
					continue;
				}
				else if (cur - k * m > k)
				{

				}
				if (k <= 0)break;
				cur = k * m;
			}
			break;
		}
		if (k <= 0)break;
	}
	cout << ans << endl;
}

我们发现当前数为 km+k时本轮必败。而其他值总可以让下一次对手取时为km+k。

但是对手赢了,下波到我们了,我们还是必败呢:

下波是km,可以是(k-1)*m + k-1

当 km == (k-1)*m + k-1 时

m == k-1

是可以发生的。

其实尽可能贪心了,但是无法预估下一轮是否我们是必败,要必败就亏了呀。而要处理这个,就没有普遍做法了。所以放弃吧~~

————

题解 SP122 【STEVE - Voracious Steve】 - 洛谷专栏 (luogu.com.cn)

dfs表示 [ cur ][ 先手取多少个 ][ 后手取了多少个 ] 这种情况先手能拿的最大值

而这种情况能取到的最大值也是 依附于 其他情况的。

这是递归。

卡时间同时每种情况答案固定,所以记忆化。

代码:

#include<string.h>

#define ll long long
//#define int long long
#define PII pair<int,int>
//纵深
const int mod = 998244353;
const int maxn = 105;
int dp[maxn][maxn][maxn];
int n, m;

int dfs(int cur, int fir, int sec)
{
	if (cur == 0)//当先手可以取完时,就不需要下一轮了。
		return 0;
	if (dp[cur][fir][sec]>=0)
		return dp[cur][fir][sec];
	int ans = 0;
	for (int i = 1; i <= min(m,cur-fir-sec); i++)
	{
		if (i + fir + sec == cur)
		{
			ans = max(ans, cur - dfs(sec, 0, 0));//当前减去另一个人能拿的最大值,就是我们拿的最大值
		}
		else
		{
			ans = max(ans, cur - dfs(cur,sec,fir+i));//接着拿。。?
		}
	}
	return dp[cur][fir][sec] = ans;
}

void solve()
{
	memset(dp, 255, sizeof dp);//每个字节8bit都置1,是个负数
	cin >> n >> m;
	cout << dfs(n, 0, 0) << endl;
}

int main()
{
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	int t = 1;
	cin >> t;
	while (t--)
	{
		solve();
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值