游戏

题意

给定一个集合 \(S,|S|=n\)

\(m\) 轮操作,两人交替操作。

每一轮有一个参数 \(b\),每次进行两种操作:保留 \(S\) 中满足 \(b\mid x\) 的数或保留 \(S\) 中满足 \(b\nmid x\) 的数。

先手希望最终 \(S\) 内元素总和尽可能小,后手希望尽可能大。

问两人都采取最优策略,最终的值是多少。

思路

在这之前,我一直都不知道博弈论的题怎么写暴力。

这道题其实就是爆搜即可。

对于每一步,我们都搜可能的两种状态,然后当前层如果是先手,接受较小的;后手,接受较大的。

如果哪个集合没有东西了,直接返回 \(0\)

一共 \(m\) 层,由于没有东西直接 return 了,所以每一层都是 \(n\) 个数,复杂度 \(O(nm)\)

实际上,当 \(m>2\lg n\) 时,答案就是 \(0\)

先手每一次都会使得集合大小减半,最后一定会变成 \(0\),考虑交替操作,所以结果 \(\le 0\),对于后者,同理,\(\ge 0\),所以 \(=0\)

#include<iostream>
#include<vector>
using namespace std;
const int N=20010,M=30;
typedef long long ll;
const ll INF=1e18;
int n,m,cnt;
ll b[M];
ll dfs(vector<ll>&a,int d){
	vector<ll>t1,t2;
	++cnt;
	// printf("node=%d,dep=%d\n",cnt,d);
	ll s1=INF,s2=INF;
	for(ll v:a)
		if(v%b[d]==0)t1.push_back(v);
		else t2.push_back(v);
	// printf("div by %lld,s1=%lld\n",b[d],s1);
	// for(ll v:t1)printf("%d ",v);
	// puts("\nother");
	// for(ll v:t2)printf("%d ",v);
	// puts("\n\n\n");
	if(t1.empty())s1=0;
	if(t2.empty())s2=0;
	if(d==m){
		s1=s2=0;
		for(ll v:t1)s1+=v;
		for(ll v:t2)s2+=v;
		return d&1?min(s1,s2):max(s1,s2);
	}
	if(s1==INF)s1=dfs(t1,d+1);
	if(s2==INF)s2=dfs(t2,d+1);
	return d&1?min(s1,s2):max(s1,s2);
}
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>n>>m;
	if(m>28){
		putchar('0');
		return 0;
	}
	vector<ll>a(n);
	for(ll &v:a)cin>>v;
	for(int i=1;i<=m;++i)cin>>b[i];
	cout<<dfs(a,1);
	// dfs(a,1);
	return 0;
}
posted @ 2023-11-17 12:57  wscqwq  阅读(3)  评论(0编辑  收藏  举报