gems gems gems
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 1172 Accepted Submission(s): 242
They place the gems in a row and decide to take turns to take gems from left to right.
Alice goes first and takes 1 or 2 gems from the left. After that, on each turn a player can take k or k+1 gems if the other player takes k gems in the previous turn. The game ends when there are no gems left or the current player can't take k or k+1 gems.
Your task is to determine the difference between the total value of gems Alice took and Bob took. Assume both players play optimally. Alice wants to maximize the difference while Bob wants to minimize it.
For each test case:
the first line contains a numbers n (1≤n≤20000);
the second line contains n numbers: V1,V2…Vn. (−100000≤Vi≤100000)
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<ctype.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); }
#define MS(x, y) memset(x, y, sizeof(x))
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b > a)a = b; }
template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b < a)a = b; }
const int N = 2e4 + 10, M = 0, Z = 1e9 + 7, inf = 0x3f3f3f3f;
template <class T1, class T2>inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; }
int casenum, casei;
int n;
int sum[N];
int v[N];
int f[N][202];
int main()
{
scanf("%d", &casenum);
for (casei = 1; casei <= casenum; ++casei)
{
scanf("%d", &n);
for (int i = n; i >= 1; --i)scanf("%d", &v[i]);
for (int i = 1; i <= n; ++i)sum[i] = sum[i - 1] + v[i];
//設m爲最大能取到的數量,則有(1+m)*m<=n*2,得到m^2+m<=n*2,得到m<sqrt(n*2)
int m = sqrt(n * 2);
while ((1 + m) * m > n * 2)--m;
for (int i = 1; i <= n; ++i)//枚舉當前還剩下幾個
{
int top = min(i, m);
for (int j = 1; j <= top; ++j)//枚舉上一輪的人拿了多少個
{
f[i][j] = -2e9;
gmax(f[i][j], (sum[i] - sum[i - j]) - f[i - j][j]); //這一輪拿一樣多
if(i > j)gmax(f[i][j], (sum[i] - sum[i - j - 1]) - f[i - j - 1][j + 1]); //這一輪多拿一個
}
}
printf("%d\n", f[n][1]);
}
return 0;
}
/*
【題意】
有n(2e4)個寶石
兩個人輪流從左側取寶石,Alice先手,首輪取1個或2個寶石,
如果上一輪取了k個寶石,則這一輪只能取k或k+1個寶石。
一旦不能再取寶石就結束。
雙方都希望自己拿到的寶石數比對方儘可能多。
問你,先手比後手多拿的最大寶石數。
【分析】
我們用f[i][j]表示剩下i和寶石沒取,上個人取了j個寶石,先手所能獲得的最大寶石價值差。
則我們此時,如果可i>=j(或i>=j+1),則可以選擇拿j或j+1個寶石,對應着f[i-k][k]的後繼,有gmax(f[i][j], (sum[i]-sum[i-k]) - f[i-k][k])
1,所有非法狀態,都以0爲初始值即可
2,每個v最多使得絕對值偏差1e5,所以int即可保存所有狀態
【時間複雜度&&優化】
O(nsqrt(n))
*/