UVa 10891 (博弈+DP) Game of Sum
最开始的时候思路就想错了,就不说错误的思路了。
因为这n个数的总和是一定的,所以在取数的时候不是让自己尽可能拿的最多,而是让对方尽量取得最少。
记忆化搜索(时间复杂度O(n3)):
d(i, j)表示原序列中第i个元素到第j个元素构成的子序列,先手取数能够得到的最大值。
sum(i, j) 表示从第i个元素到第j个元素的和
因为要让对手获得最小的分数,所以状态转移方程为:
d(i, j) = sum(i, j) - min{d(枚举所有可能剩给对手的序列), 0(0代表全部取完)}
s数组保存a中前i个元素的和,这样sum(i, j) = s[j] - s[i-1]
1 #define LOCAL 2 #include <iostream> 3 #include <cstdio> 4 #include <cstring> 5 #include <algorithm> 6 using namespace std; 7 8 const int maxn = 100 + 10; 9 int a[maxn], s[maxn], d[maxn][maxn], vis[maxn][maxn]; 10 11 int dp(int i, int j) 12 { 13 if(vis[i][j]) 14 return d[i][j]; 15 vis[i][j] = 1; 16 int m = 0; 17 for(int k = i + 1; k <= j; ++k) 18 m = min(m, dp(k, j)); 19 for(int k = j - 1; k >= i; --k) 20 m = min(m, dp(i, k)); 21 d[i][j] = s[j] - s[i-1] - m; 22 return d[i][j]; 23 } 24 25 int main(void) 26 { 27 #ifdef LOCAL 28 freopen("10891in.txt", "r", stdin); 29 #endif 30 31 int n; 32 while(scanf("%d", &n) == 1 && n) 33 { 34 s[0] = 0; 35 for(int i = 1; i <= n; ++i) 36 { 37 scanf("%d", &a[i]); 38 s[i] = s[i-1] + a[i]; 39 } 40 memset(vis, 0, sizeof(vis)); 41 printf("%d\n", 2*dp(1, n) - s[n]); 42 } 43 return 0; 44 }
递推(时间复杂度O(n2)):
令f(i, j) = min{d(i, j), d(i+1, j),,,d(j, j)}
g(i, j) = min{d(i, j), d(i, j-1),,,d(i, i)}
则状态转移方程可写成:
d(i, j) = min{f(i+1, j), g(i, j-1), 0}
f和g的递推为:
f(i, j) = min{d(i, j), f(i+1, j)}
g(i, j) = min{d(i, j), g(i, j-1)}
1 //#define LOCAL 2 #include <iostream> 3 #include <cstdio> 4 #include <cstring> 5 #include <algorithm> 6 using namespace std; 7 8 const int maxn = 100 + 10; 9 int a[maxn], s[maxn], d[maxn][maxn], f[maxn][maxn], g[maxn][maxn]; 10 11 int main(void) 12 { 13 #ifdef LOCAL 14 freopen("10891in.txt", "r", stdin); 15 #endif 16 17 int n; 18 while(scanf("%d", &n) == 1 && n) 19 { 20 s[0] = 0; 21 for(int i = 1; i <= n; ++i) 22 { 23 scanf("%d", &a[i]); 24 s[i] = s[i-1] + a[i]; 25 } 26 for(int i = 1; i <= n; ++i)//边界 27 d[i][i] = f[i][i] = g[i][i] = a[i]; 28 for(int L = 1; L < n; ++L) 29 for(int i = 1; i + L <= n; ++i) 30 { 31 int j = i + L; 32 int m = 0; 33 m = min(m, f[i+1][j]); 34 m = min(m, g[i][j-1]); 35 d[i][j] = s[j] - s[i-1] - m; 36 //更新f和g 37 f[i][j] = min(d[i][j], f[i+1][j]); 38 g[i][j] = min(d[i][j], g[i][j-1]); 39 } 40 41 printf("%d\n", 2*d[1][n] - s[n]); 42 } 43 return 0; 44 }