首页 > 代码库 > uva10891 - Game of Sum(递推,极大极小的思想)

uva10891 - Game of Sum(递推,极大极小的思想)

题目:uva10891 - Game of Sum(递推)


题目大意:给出N个数,然后有两个小伙伴在玩游戏,每次可以从这一排数字的两侧中选择一侧开始取连续的数,必须取一个,也可以取完。这两个小伙伴都会采用最优的策略来取数,问第一个小伙伴取数的和与第2个小伙伴取数的和的差值。


解题思路:这题刚开始没什么头绪,只要碰到博弈思想的题目就没什么想法。看了别人的题解才明白。

                  先从简单的情况来讲如果每个小伙伴只能从两侧的一侧取一个数的话, dp【i】【j】:小伙伴在第i个数字到第j个数字能取得的最大值; sum【i】【j】 :第i个数字到第j个数字的连续和。

                  那么递推式dp【i][j] = sum【i】【j】 - min(dp【i + 1】【j】, dp【i】【j - 1】);

                  这题是可以取连续的多个数字。递推式: dp【i】【j】 = sum【i】【j】 - min (dp[i + k][j], dp[i][j - k], 0);  k>= 1 && k <= j - i - 1.  0的情况代表的是全部的数都选择的情况。这里为什么不是取最大的和,而是要用总的和减去最小的最大值,我想是因为递推的需要,并且第一个小伙伴取完剩下的给另一个小伙伴选他肯定也是选择最大的,所以第一个小伙伴取完数后要使得剩下的最大值最小的,这样第一个小伙伴就可以拿到更多,同时第二个就拿到的更少。


代码:

#include <cstdio>
#include <cstring>

typedef long long ll;
const int N = 105;
const ll INF = 0x3f3f3f3f;

ll dp[N][N], sum[N];
int v[N];
int n;

void init () {

	memset (sum , 0, sizeof (sum));
	for (int i = n; i >= 1; i--) {

		if (i == n)
			sum[i] = v[i];
		else
			sum[i] = sum[i + 1] + v[i];
	//	printf ("%lld\n", sum[i]);
		dp[i][i] = v[i]; 
	}
}

ll Min (const ll a, const ll b) { return a < b? a: b; }

int main () {

	while (scanf ("%d", &n) && n) {

		for (int i = 1; i <= n; i++)
			scanf ("%d", &v[i]);
		init ();

		ll mm;
		for (int len = 1; len < n; len++)
			for (int i = 1; i + len <= n; i++) {
		
				mm = INF;
//				printf ("%lld\n", mm);
				for (int k = 1; k <= len; k++)
					mm = Min (mm, Min (dp[i + k][i + len], dp[i][i + len - k]));

				dp[i][i + len] = sum[i] - sum[i + len + 1] - Min (0, mm);  
			}

//		printf ("%lld\n", dp[1][n]);
		printf ("%lld\n", 2 * dp[1][n] - sum[1]);

	}
	return 0;
}