UVa 10891 Game of Sum(博弈区间DP)

来源:互联网 发布:重庆招聘软件开发 编辑:程序博客网 时间:2024/05/16 07:04

题意:

给定n个数字,A和B可以从这串数字的两端任意选数字,一次只能从一端选取。

并且A B都尽力使自己选择的结果为最大的,可以理解成A B每一步走的都是最优的。

如果A先选择,则A B差值最大是多少。

思路:dp[i][j]维护玩家A可以选的最大值。初始化为0,表示什么都没有选,所以值为0。然后对每个区间(i,j),选取其中的点k扫描其子区间。因为我们要找的是最大的值,所以在扫描时我们找到最小的dp值,然后再从和里减去,从而得到A可以在该区间选取的最大sum。

转移方程:dp[i][j] = sum(i, j) - min(0, dp[i][k], dp[k+1][j]),其中i <= k < j。

代码:

记忆化搜索姿势:用记忆化搜索的话,dp初始化为-1。

#include <algorithm>#include <iostream>#include <sstream>#include <cstring>#include <cstdio>#include <vector>#include <string>#include <queue>#include <stack>#include <cmath>#include <set>#include <map>using namespace std;typedef long long LL;#define mem(a, n) memset(a, n, sizeof(a))#define ALL(v) v.begin(), v.end()#define si(a) scanf("%d", &a)#define sii(a, b) scanf("%d%d", &a, &b)#define siii(a, b, c) scanf("%d%d%d", &a, &b, &c)#define pb push_back#define eps 1e-8const int inf = 0x3f3f3f3f, N = 1e3 + 5, MOD = 1e9 + 7;int T, cas = 0;int n, m;int dp[N][N], a[N], sum[N];int dfs(int i, int j) {int& ret = dp[i][j];if(ret != -1) return ret;int tmp = 0;for(int k = i; k < j; k ++)tmp = min(tmp, min(dfs(i, k), dfs(k + 1, j)));return ret = sum[j] - sum[i-1] - tmp;}int main(){#ifdef LOCAL    freopen("/Users/apple/input.txt", "r", stdin);//  freopen("/Users/apple/out.txt", "w", stdout);#endif    while(si(n), n) {    mem(sum, 0);    mem(dp, -1);    for(int i = 1; i <= n; i ++) si(a[i]), sum[i] = sum[i-1] + a[i];    for(int i = 1; i <= n; i ++) dp[i][i] = a[i];    printf("%d\n", 2 * dfs(1, n) - sum[n]);    }        return 0;}

另一姿势:

 while(si(n), n) {    mem(sum, 0);    mem(dp, 0);    for(int i = 1; i <= n; i ++) si(a[i]), sum[i] = sum[i-1] + a[i];    for(int i = 1; i <= n; i ++) dp[i][i] = a[i];    for(int l = 2; l <= n; l ++) {    for(int i = 1; i < n; i ++) {    int j = i + l - 1;    for(int k = i; k < j; k ++)    dp[i][j] = min(dp[i][j], min(dp[i][k], dp[k+1][j]));    dp[i][j] = sum[j] - sum[i-1] - dp[i][j];    }    }    printf("%d\n", 2 * dp[1][n] - sum[n]);}

0 0
原创粉丝点击