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;
}
posted @ 2022-08-05 21:05  One_Zzz  阅读(58)  评论(0)    收藏  举报