每日更新蓝桥杯题解, 有兴趣关注一波呀
题目描述
逗志芃又一次面临了危机。逗志芃的妹子是个聪明绝顶的人,相比之下逗志芃就很菜了。现在她妹子要和他玩一个游戏,这个游戏是这样的:一共有n个数(n是偶数)写成一行,然后两个人轮流取数,每次只能从最前面或者最后面取走一个数,全部取完则游戏结束,之后每个人取走的数的和就是每个人的得分。由于逗志芃妹子很厉害,但他又不想输,所以只能找到你了,你要告诉他最多可以得到多少分。(注意,妹子智商是maxlongint所以是不会犯错的,每次的策略必然最优,而且逗志芃是先手)
题解
区间dp问题。
设f(i, j)是从(i, j)可以我取得的最大值。那么可以分成两种情况讨论
- 轮到我
- 轮到妹妹
如果轮到了我,那么我有两种选择,要么从左边,要么从右边
- f ( i + 1 , j ) + a [ i ] f(i + 1, j) + a[i] f(i+1,j)+a[i]
- f ( i , j − 1 ) + a [ j ] f(i, j - 1) + a[j] f(i,j−1)+a[j]
两者取最大值,没有什么好说的。
如果轮到了妹妹,那么妹妹也有同样的两种选择,由于妹妹不是弱智,所以也会找最优方案,那么,从我的角度来说,就是从
- f ( i + 1 , j ) f(i + 1, j) f(i+1,j)
- f ( i , j − 1 ) f(i, j - 1) f(i,j−1)
两者中找最小值了。注意这里不加,因为是对我来说,妹妹取东西对我没贡献,并且,妹妹要尽量大,我只能尽量小。
这里参考了作者:陆小路-1的博客。我是想不出来分这两种情况讨论dp的,但是划分之后就成了简单的区间dp。他的代码是r作为外层循环,有点反人类,这里改成l做成外层循环了。
java代码
import java.util.Scanner;
/**
* @author: Zekun Fu
* @date: 2022/10/17 0:13
* @Description: xxx的危机
*/
public class WeiJiOfxxx {
public static void main(String args[]) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int[] a = new int[n];
int[][] f = new int[n][n];
for (int i = 0; i < n; i++) {
a[i] = sc.nextInt();
}
for (int i = n - 1; i >= 0; i--) { // [i,j]区间dp
for (int j = i; j < n; j++) {
if (i == j) f[i][j] = 0;
else {
if (((j - i) & 1) == 1) {
f[i][j] = Math.max(f[i + 1][j] + a[i], f[i][j - 1] + a[j]);
}
else {
f[i][j] = Math.min(f[i + 1][j], f[i][j - 1]);
}
}
}
}
System.out.println(f[0][n - 1]);
}
}
C++代码
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1005;
int f[maxn][maxn];
int a[maxn];
int main() {
int n;
scanf("%d", &n);
for (int i = 0; i < n; i++) {
scanf("%d", &a[i]);
}
for (int r = 0; r < n; r++) {
for (int l = r; l >= 0; l--) {
if (l == r)
f[l][r] = 0; // 如果只剩一个,我的最大得分为0
else {
if ((r - l) & 1) f[l][r] = max(f[l + 1][r] + a[l], f[l][r - 1] + a[r]);
else f[l][r] = min(f[l + 1][r], f[l][r - 1]); // 我不得分,并且对面取最大的,我只能得最小的
}
}
}
printf("%d\n", f[0][n - 1]);
}