CF729F Financiers Game
\(n\) 这么小,意识到可能不是猜结论。考虑暴力动态规划。
设计状态:\(i,j,k\) 代表左边取了 \(i\) 个,右边取了 \(j\) 个,现在的 \(k\) 值。\(f_{i,j,k}\) 和 \(g_{i,j,k}\) 分别代表当前是 MAXer 取还是 Miner 取。
那转移大概就是说有 \(k\gets k\) 和 \(k\gets k+1\) 两种。形式化的转移:
\[\begin{cases}f_{i,j,k}=\min(g_{i,j-k,k}-\Sigma(j-k,j],g_{i,j-k-1,k+1}-\Sigma(j-k-1,j])\\g_{i,j,k}=\min(g_{i+k,j,k}+\Sigma[i+k,i),g_{i+k+1,j,k+1}+\Sigma[i+k+1,i))\end{cases}
\]
其中 \(\Sigma[l,r]=\sum_{i=l}^ra_i\)。
空间 \(n^3\),这是显然开不下的。(接下来需要一点技巧)
由 \(1+\cdots+\sqrt n=\mathcal O(n)\) 可知,\(k\) 是 \(\sqrt n\) 级别的,空间复杂度优化至 \(\mathcal O(n^2\sqrt n)\)。
然后再观察,发现 \(i,j\) 的差也是 \(\mathcal O(k)=\mathcal O(\sqrt n)\) 级别的,空间复杂度优化至 \(\mathcal O(n^2)\)。
这个题不是很好写 \(\tt dp\),考虑写记忆化搜索。
\(\tt dp\) 数组也不是很好开,可以对状态 \(\{i,j,k\}\) 进行哈希,然后对 \(10^7\) 级别的大数取模,然后就可以开下了。
#include<bits/stdc++.h>
using namespace std;
using hsh = size_t;
const hsh v1 = 3817,v2 = 1243,v3 = 2029,v4 = 2839,v5 = 6641,v6 = 3529,v7 = 1431;
struct status{
int i,j,k;
status(int i,int j,int k) : i(i),j(j),k(k){}
hsh HASH(){ return (((4000LL * i) + j) * 100) + k; }
};
const int N = 4e3 + 5;
const hsh mod = 20090729;
int n,s[N];
int f[mod],g[mod];
int dfs(int i,int j,int k,bool who){
if(j - i + 1 < k) return 0;
if(j - i + 1 == k) return (who ? -1 : 1) * (s[j] - s[i - 1]);
hsh val = status(i,j,k).HASH() % mod;
if(who){
if(f[val] != -1) return f[val];
f[val] = dfs(i,j - k,k,0) - (s[j] - s[j - k]);
f[val] = min(f[val],dfs(i,j - k - 1,k + 1,0) - (s[j] - s[j - k - 1]));
} else {
if(g[val] != -1) return g[val];
g[val] = dfs(i + k,j,k,1) + (s[i + k - 1] - s[i - 1]);
g[val] = max(g[val],dfs(i + k + 1,j,k + 1,1) + (s[i + k] - s[i - 1]));
}
return who ? f[val] : g[val];
}
int main(){
scanf("%d",&n);
for(int i = 1;i <= n;++i){
scanf("%d",s + i);
s[i] += s[i - 1];
}
memset(f,-1,sizeof f);
memset(g,-1,sizeof g);
printf("%d\n",dfs(1,n,1,0));
return 0;
}

浙公网安备 33010602011771号