题意:
n<=100的整数序列,A和B轮流取数,每个人只能从左端或者右端取任意数量的数
A先手,所有数取完游戏结束,假设两个人都采取最优策略,求SumA−SumB
分析:
区间dp,dp[l][r]:=先手A从s[l...r]序列取得的最大值
转移的话,枚举给后手B留下的序列,最小的那个被减去后就是先手取到的
dp[l][r]:=sum[r]−sum[l−1]−
min{0(取完),min{dp[l+1][r],dp[l+2][r],...,dp[r][r](右边取)},min{dp[l][r−1],dp[l][r−2],...,dp[l][l]}(左边取)};
代码:
//
// Created by TaoSama on 2015-11-13
// Copyright (c) 2015 TaoSama. All rights reserved.
//
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>
using namespace std;
#define pr(x) cout << #x << " = " << x << " "
#define prln(x) cout << #x << " = " << x << endl
const int N = 1e5 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;
int n, a[105], sum[105], dp[105][105];
int dfs(int l, int r) {
int &ret = dp[l][r];
if(ret != INF) return ret;
int left = 0;
for(int i = l; i < r; ++i) left = min(left, dfs(l, i));
for(int i = l + 1; i <= r; ++i) left = min(left, dfs(i, r));
ret = sum[r] - sum[l - 1] - left;
return ret;
}
int main() {
#ifdef LOCAL
freopen("C:\\Users\\TaoSama\\Desktop\\in.txt", "r", stdin);
// freopen("C:\\Users\\TaoSama\\Desktop\\out.txt","w",stdout);
#endif
ios_base::sync_with_stdio(0);
int t; scanf("%d", &t);
int kase = 0;
while(t--) {
scanf("%d", &n);
for(int i = 1; i <= n; ++i) scanf("%d", a + i), sum[i] = sum[i - 1] + a[i];
memset(dp, 0x3f, sizeof dp);
printf("Case %d: %d\n", ++kase, 2 * dfs(1, n) - sum[n]);
}
return 0;
}
观察发现可以优化到O(n2)
用右边取f[i][j]:=min{dp[i][j],dp[i+1][j],dp[i+2][j],...,dp[j][j]};
用左边取g[i][j]:=min{dp[i][j],dp[i][j−1],dp[i][j−2],...,dp[i][i]};
转移就很显然了,f[i][j]=min(f[i+1][j],dp[i][j]),g[i][j]=min(g[i][j−1],dp[i][j])
代码:
//
// Created by TaoSama on 2015-11-13
// Copyright (c) 2015 TaoSama. All rights reserved.
//
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>
using namespace std;
#define pr(x) cout << #x << " = " << x << " "
#define prln(x) cout << #x << " = " << x << endl
const int N = 1e5 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;
int n, a[105], sum[105];
int f[105][105], g[105][105], dp[105][105];
//f[i][j]:= min{dp[i][j],dp[i+1][j],dp[i+2][j],...,dp[j][j]};
//g[i][j]:= min{dp[i][j],dp[i][j-1],dp[i][j-2],...,dp[i][i]};
int main() {
#ifdef LOCAL
freopen("C:\\Users\\TaoSama\\Desktop\\in.txt", "r", stdin);
// freopen("C:\\Users\\TaoSama\\Desktop\\out.txt","w",stdout);
#endif
ios_base::sync_with_stdio(0);
int t; scanf("%d", &t);
int kase = 0;
while(t--) {
scanf("%d", &n);
for(int i = 1; i <= n; ++i) {
scanf("%d", a + i);
sum[i] = sum[i - 1] + a[i];
dp[i][i] = f[i][i] = g[i][i] = a[i];
}
for(int l = 1; l < n; ++l) {
for(int i = 1; i + l <= n; ++i) {
int j = i + l;
dp[i][j] = sum[j] - sum[i - 1];
dp[i][j] -= min(0, min(f[i + 1][j], g[i][j - 1]));
f[i][j] = min(f[i + 1][j], dp[i][j]);
g[i][j] = min(g[i][j - 1], dp[i][j]);
}
}
printf("Case %d: %d\n", ++kase, 2 * dp[1][n] - sum[n]);
}
return 0;
}