游戏
题意
给定一个集合 \(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;
}