【HDU6199 2017 ACM ICPC Asia Regional Shenyang Online F】【博弈 DP】gems gems gems 雙人從左側拿寶石 每次拿相同或加一的最小差值

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


Problem Description
Now there are n gems, each of which has its own value. Alice and Bob play a game with these n gems.
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.
 

Input
The first line contains an integer T (1T10), the number of the test cases. 
For each test case:
the first line contains a numbers n (1n20000);
the second line contains n numbers: V1,V2Vn. (100000Vi100000)
 

Output
For each test case, print a single number in a line: the difference between the total value of gems Alice took and the total value of gems Bob took.
 

Sample Input
1 3 1 3 2
 

Sample Output
4
 

Source

#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))

*/


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章