喵哈哈村的挑衅(区间DP)

B - 喵哈哈村的挑衅
Time Limit: 60000/30000MS (Java/Others) Memory Limit: 128000/64000KB (Java/Others)
Submit Status
Problem Description

喵哈哈村就挨着哗啦啦村,他们很喜欢和哗啦啦村互黑~
今天也不例外,喵哈哈派了智商流选手 青君 去挑衅哗啦啦村
哗啦啦村当然不会畏惧这种弱的挑衅,于是派出了谈笑风生的 狗哥 应战!
青君和狗哥站在一个空地上,空地上有两排物品,每排都有n个,每个物品都有一定价值
每次每个人只能拿第一排的最左边的物品,或者拿第一排最右边的物品,或者第二排最左边的物品,或者第二排最右边的物品
狗哥 和 青君轮流拿~
青君先手,请问在狗哥绝顶聪明的情况下,青君所拿物品的最大价值是多少?
Input

多组测试数据,最多100组
第一行 一个数字n,表示每排有多少个物品。
第二行 n个数字,a[i]表示第一排,第i个物品的价值是多少
第三行 n个数字,b[i]表示第二排,第i个物品的价值是多少
1<=n<=25 1<=a[i]<=1000 1<=b[i]<=1000
Output

对于每组测试数据,输出一个整数,表示青君所能获得的最大价值是多少
Sample Input

1
23
53
3
10 100 20
2 4 3
Sample Output

53
105

题目大意:有两排物品,每排都有n个,青君和狗哥轮流从每排的两侧拿任意一个物品。青君先手,假设狗哥绝顶聪明,青君所拿物品价值之和最大为多少?
分析:区间DP。
状态:dp[x1][y1][x2][y2],表示在区间[x1, y1]和区间[x2, y2]中能取到的最大值。
状态转移方程:有四种情况,分别为x1+1,y1-1,x2+1,y2-1。显然取四个中最小的那个。
下面的代码里,分别用a,b数组来表示两排的前缀和。

代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

int n, a[30], b[30], dp[30][30][30][30];

int solve(int x1, int y1, int x2, int y2) {
    if(dp[x1][y1][x2][y2] != -1) return dp[x1][y1][x2][y2];
    if(x1 > y1 || x2 > y2) dp[x1][y1][x2][y2] = 0;
    int ans = 0;
    int sum = 0;
    if(x1 <= y1) sum += a[y1] - a[x1-1];
    if(x2 <= y2) sum += b[y2] - b[x2-1];
    if(x1 <= y1) {
        ans = max(ans, sum-solve(x1+1, y1, x2, y2));
        ans = max(ans, sum-solve(x1, y1-1, x2, y2));
    }
    if(x2 <= y2) {
        ans = max(ans, sum-solve(x1, y1, x2+1, y2));
        ans = max(ans, sum-solve(x1, y1, x2, y2-1));
    }
    return dp[x1][y1][x2][y2] = ans;
}

int main() {
    while(scanf("%d", &n) != EOF) {
        memset(dp, -1, sizeof(dp));
        memset(a, 0, sizeof(a));
        memset(b, 0, sizeof(b));
        for(int i = 1; i <= n; i++) {
            int x;
            scanf("%d", &x);
            a[i] = a[i-1] + x;
        }
        for(int i = 1; i <= n; i++) {
            int x;
            scanf("%d", &x);
            b[i] = b[i-1] + x;
        }
        printf("%d\n", solve(1, n, 1, n));
    }
    return 0;
}

你可能感兴趣的:(dp)