【蓝桥杯】试题 历届真题 取球博弈【第七届】【省赛】【A组】

博弈问题,去模拟博弈过程,我走一步你走一步,走的都是对自己最有利的。用一个person变量来表示该谁走了,person=1是先手,person=0是后手。比较详细的注释写在最后一个想法里了。

第一个想法:纯暴力,用例2/8

#include<iostream>
#include<vector>
#include<string>
#include<map>
#include<set>
using namespace std;

//person=1表示先手的人
int dfs(int sum, int n1, int n2, int n3,int person,int sum1,int sum2)
{
	int min1 = min(n1, min(n2, n3));
	if (sum < min1)
	{
		if (sum1 % 2 == 1&&sum2%2!=1)
		{
			return 1;
		}
		if (sum2 % 2 == 1 && sum1 % 2 != 1)
		{
			return -1;
		}
		return 0;
	}
	if (person == 1)
	{
		int a = 2, b = 2, c = 2;
		if (sum >= n1)
		{
			a = dfs(sum - n1, n1, n2, n3, !person, sum1 + n1, sum2);
		}
		if (sum >= n2)
		{
			b = dfs(sum - n2, n1, n2, n3, !person, sum1 + n2, sum2);
		}
		if (sum >= n3)
		{
			c = dfs(sum - n3, n1, n2, n3, !person, sum1 + n3, sum2);
		}
		
		//cout << sum << " " << person << " " << sum1 << " " << sum2 << " a:"<<a<<" b:"<<b<<" c:"<<c << endl;
		if (a == 1 || b == 1 || c == 1)
		{
			return 1;
		}
		else if ((a == 0&&a!=2) || (b==0&&b!=2) || (c==0&&c!=2))
		{
			return 0;
		}
		else
		{
			return -1;
		}
	}
	else
	{
		int a = 2, b = 2, c = 2;
		if (sum >= n1)
		{
			a = dfs(sum - n1, n1, n2, n3, !person, sum1, sum2+n1);
		}
		if (sum >= n2)
		{
			b = dfs(sum - n2, n1, n2, n3, !person, sum1, sum2+n2);
		}
		if (sum >= n3)
		{
			c = dfs(sum - n3, n1, n2, n3, !person, sum1, sum2+n3);
		}
		//cout << sum << " " << person << " " << sum1 << " " << sum2 << " a:" << a << " b:" << b << " c:" << c << endl;

		if (a == -1 || b == -1 || c == -1)
		{
			return -1;
		}
		else if ((a == 0 && a != 2) || (b == 0 && b != 2) || (c == 0 && c != 2))
		{
			return 0;
		}
		else
		{
			return 1;
		}
	}
	
}
int main()
{
	int n1, n2, n3;
	cin >> n1 >> n2 >> n3;
	vector<char>ans;
	for (int i = 0; i < 5; i++)
	{
		int x = 0;
		cin >> x;
		int tmp = dfs(x, n1, n2, n3, 1, 0, 0);
		if (tmp == 1)
		{
			ans.push_back('+');
		}
		else if (tmp == 0)
		{
			ans.push_back('0');
		}
		else
		{
			ans.push_back('-');
		}
	}


	for (int i = 0; i < 5; i++)
	{
		cout << ans[i] << " ";
	}
	return 0;
}

第二个想法:备忘录剪枝,用例4/8

纯暴力过的用例太少,就想到了这个状态可能会有重复,用备忘录去重。

#include<iostream>
#include<vector>
#include<string>
#include<map>
#include<set>
using namespace std;

//person=1表示先手的人
class memo
{
public:
	int sum;
	int person;
	int sum1;
	int sum2;
	int res;
	memo(int a, int b, int c, int d, int e)
	{
		sum = a;
		person = b;
		sum1 = c;
		sum2 = d;
		res = e;
	}
};
vector<memo>memory;
int dfs(int sum, int n1, int n2, int n3,int person,int sum1,int sum2)
{
	int min1 = min(n1, min(n2, n3));
	for (int i = 0; i < memory.size(); i++)
	{
		if (sum == memory[i].sum && person == memory[i].person && sum1 == memory[i].sum1 && sum2 == memory[i].sum2)
		{
			return memory[i].res;
		}
	}
	if (sum < min1)
	{
		if (sum1 % 2 == 1&&sum2%2!=1)
		{
			memo m(sum, person, sum1, sum2,1);
			memory.push_back(m);
			return 1;
		}
		if (sum2 % 2 == 1 && sum1 % 2 != 1)
		{
			memo m(sum, person, sum1, sum2, -1);
			memory.push_back(m);
			return -1;
		}
		memo m(sum, person, sum1, sum2, 0);
		memory.push_back(m);
		return 0;
	}
	if (person == 1)
	{
		int a = 2, b = 2, c = 2;
		if (sum >= n1)
		{
			a = dfs(sum - n1, n1, n2, n3, !person, sum1 + n1, sum2);
		}
		if (sum >= n2)
		{
			b = dfs(sum - n2, n1, n2, n3, !person, sum1 + n2, sum2);
		}
		if (sum >= n3)
		{
			c = dfs(sum - n3, n1, n2, n3, !person, sum1 + n3, sum2);
		}
		
		//cout << sum << " " << person << " " << sum1 << " " << sum2 << " a:"<<a<<" b:"<<b<<" c:"<<c << endl;
		if (a == 1 || b == 1 || c == 1)
		{
			memo m(sum, person, sum1, sum2, 1);
			memory.push_back(m);
			return 1;
		}
		else if (a == 0 || b==0|| c==0)
		{
			memo m(sum, person, sum1, sum2, 0);
			memory.push_back(m);
			return 0;
		}
		else
		{
			memo m(sum, person, sum1, sum2, -1);
			memory.push_back(m);
			return -1;
		}
	}
	else
	{
		int a = 2, b = 2, c = 2;
		if (sum >= n1)
		{
			a = dfs(sum - n1, n1, n2, n3, !person, sum1, sum2+n1);
		}
		if (sum >= n2)
		{
			b = dfs(sum - n2, n1, n2, n3, !person, sum1, sum2+n2);
		}
		if (sum >= n3)
		{
			c = dfs(sum - n3, n1, n2, n3, !person, sum1, sum2+n3);
		}
		//cout << sum << " " << person << " " << sum1 << " " << sum2 << " a:" << a << " b:" << b << " c:" << c << endl;

		if (a == -1 || b == -1 || c == -1)
		{
			memo m(sum, person, sum1, sum2, -1);
			memory.push_back(m);
			return -1;
		}
		else if (a == 0 || b == 0 || c == 0)
		{
			memo m(sum, person, sum1, sum2, 0);
			memory.push_back(m);
			return 0;
		}
		else
		{
			memo m(sum, person, sum1, sum2, 1);
			memory.push_back(m);
			return 1;
		}
	}
	
}
int main()
{
	int n1, n2, n3;
	cin >> n1 >> n2 >> n3;
	vector<char>ans;
	for (int i = 0; i < 5; i++)
	{
		int x = 0;
		cin >> x;
		int tmp = dfs(x, n1, n2, n3, 1, 0, 0);
		if (tmp == 1)
		{
			ans.push_back('+');
		}
		else if (tmp == 0)
		{
			ans.push_back('0');
		}
		else
		{
			ans.push_back('-');
		}
	}


	for (int i = 0; i < 5; i++)
	{
		cout << ans[i] << " ";
	}
	return 0;
}

第三个想法:优化备忘录,用例7/8(最后一个用例暂时想不到怎么优化了)

用了备忘录之后,过的用例还是很少。又想到了是因为每次找状态都要遍历一边存储状态的数组,所以又想到了用哈希表减少这部分的开销。

#include<iostream>
#include<vector>
#include<string>
#include<map>
#include<set>
using namespace std;

//person=1表示先手的人
//vector容器里依次装入sum,person,sum1,sum2
map<vector<int>, int>map1;
int dfs(int sum, int n1, int n2, int n3, int person, int sum1, int sum2)
{
	int min1 = min(n1, min(n2, n3));
	vector<int>m1;
	//判断如果这个状态已经来过了就直接返回值。
	m1.push_back(sum); m1.push_back(person); m1.push_back(sum1); m1.push_back(sum2);
	if (map1.find(m1) != map1.end())
	{
		return map1[m1];
	}
	//结束状态,也就是拿不了了
	if (sum < min1)
	{
		if (sum1 % 2 == 1 && sum2 % 2 != 1)
		{
			vector<int>m;
			m.push_back(sum); m.push_back(person); m.push_back(sum1); m.push_back(sum2);
			map1[m] = 1;
			return 1;
		}
		if (sum2 % 2 == 1 && sum1 % 2 != 1)
		{
			vector<int>m;
			m.push_back(sum); m.push_back(person); m.push_back(sum1); m.push_back(sum2);
			map1[m] = -1;
			return -1;
		}
		vector<int>m;
		m.push_back(sum); m.push_back(person); m.push_back(sum1); m.push_back(sum2);
		map1[m] = 0;
		return 0;
	}
	//person=1,先手
	if (person == 1)
	{
		int a = 2, b = 2, c = 2;
		//将三种可能的拿法全试一次
		if (sum >= n1)
		{
			a = dfs(sum - n1, n1, n2, n3, !person, sum1 + n1, sum2);
		}
		if (sum >= n2)
		{
			b = dfs(sum - n2, n1, n2, n3, !person, sum1 + n2, sum2);
		}
		if (sum >= n3)
		{
			c = dfs(sum - n3, n1, n2, n3, !person, sum1 + n3, sum2);
		}

		//只要有一种能赢就返回1,我就走那能赢的一步就可以了。
		if (a == 1 || b == 1 || c == 1)
		{
			vector<int>m;
			m.push_back(sum); m.push_back(person); m.push_back(sum1); m.push_back(sum2);
			map1[m] = 1;
			return 1;
		}
		else if (a == 0 || b == 0 || c == 0)
		{
			//已经赢不了了,找可以平局的情况
			vector<int>m;
			m.push_back(sum); m.push_back(person); m.push_back(sum1); m.push_back(sum2);
			map1[m] = 0;
			return 0;
		}
		else
		{
			//也平不了了,怎么走都是输才会返回-1
			vector<int>m;
			m.push_back(sum); m.push_back(person); m.push_back(sum1); m.push_back(sum2);
			map1[m] = -1;
			return -1;
		}
	}
	else//person=0后手
	{
		int a = 2, b = 2, c = 2;
		if (sum >= n1)
		{
			a = dfs(sum - n1, n1, n2, n3, !person, sum1, sum2 + n1);
		}
		if (sum >= n2)
		{
			b = dfs(sum - n2, n1, n2, n3, !person, sum1, sum2 + n2);
		}
		if (sum >= n3)
		{
			c = dfs(sum - n3, n1, n2, n3, !person, sum1, sum2 + n3);
		}
		//对于后手来说赢得情况就是返回-1
		if (a == -1 || b == -1 || c == -1)
		{
			vector<int>m;
			m.push_back(sum); m.push_back(person); m.push_back(sum1); m.push_back(sum2);
			map1[m] = -1;
			return -1;
		}
		else if (a == 0 || b == 0 || c == 0)
		{
			vector<int>m;
			m.push_back(sum); m.push_back(person); m.push_back(sum1); m.push_back(sum2);
			map1[m] = 0;
			return 0;
		}
		else
		{
			vector<int>m;
			m.push_back(sum); m.push_back(person); m.push_back(sum1); m.push_back(sum2);
			map1[m] = 1;
			return 1;
		}
	}

}
int main()
{
	int n1, n2, n3;
	cin >> n1 >> n2 >> n3;
	vector<char>ans;
	for (int i = 0; i < 5; i++)
	{
		int x = 0;
		cin >> x;
		int tmp = dfs(x, n1, n2, n3, 1, 0, 0);
		if (tmp == 1)
		{
			ans.push_back('+');
		}
		else if (tmp == 0)
		{
			ans.push_back('0');
		}
		else
		{
			ans.push_back('-');
		}
	}


	for (int i = 0; i < 5; i++)
	{
		cout << ans[i] << " ";
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值