【HDU6196 2017 ACM ICPC Asia Regional Shenyang Online C】【爆搜 + 剪枝】happy happy happy 爸爸儿子轮流两头取数 爸爸想输且输少

本文探讨了一种特殊的游戏策略问题,通过动态规划和搜索算法来寻找最佳解决方案,确保游戏参与者能够在限定条件下达到预期目标。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

happy happy happy

Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 143    Accepted Submission(s): 22


Problem Description
Today, Bob plays with a child. There is a row of  n  numbers. One can takes a number from the left side or the right side in turns and gets the grade which equals to the number. Bob knows that the child always chooses the bigger number of the left side and right side. If the number from two sides is equal, child will always choose the left one.
The child takes first and the person who gets more grade wins. The child will be happy only when he wins the game. 
Bob wants to make the child happy, please help him calculate the minimal difference of their grades when he loses the game. 
 

Input
There are T test cases ( T2 ).
For each test case: 
the first line only contains a number  n  ( 1n90&&n%2==0
The second line contains  n  integers:  a1,a2an(1ai105) .
 

Output
For each test ease, you should output the minimal difference of their grades when Bob loses the game. If Bob can't lose the game, output "The child will be unhappy...".
 

Sample Input
  
4 2 1 5 3 2 2 2
 

Sample Output
  
5 The child will be unhappy...
 

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 = 100, 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 a[N];

int mn[N][N], mx[N][N];
int ST;
int LIM = 0 * CLOCKS_PER_SEC;
void init()
{
	MS(mn, 63);
	MS(mx, -63);
	for (int i = 1; i <= n + 1; ++i)
	{
		mn[i][i - 1] = mx[i][i - 1] = 0;
	}
	for (int l = n; l >= 1; --l)
	{
		for (int r = l; r <= n; ++r)
		{
			int ll = l, rr = r;
			int sub;
			if (a[ll] >= a[rr])sub = a[ll++];
			else sub = a[rr--];
			gmax(mx[l][r], a[ll] + mx[ll + 1][rr] - sub);
			gmin(mn[l][r], a[ll] + mn[ll + 1][rr] - sub);
			gmax(mx[l][r], a[rr] + mx[ll][rr - 1] - sub);
			gmin(mn[l][r], a[rr] + mn[ll][rr - 1] - sub);
		}
	}
}
int ANS;
void dfs(int l, int r, int dif)
{
	if (l > r)
	{
		gmax(ANS, dif);
		return;
	}
	if (dif + mn[l][r] >= 0) return;	//哪怕取一个最小值,都会赢了儿子,是个无效状态
	if (dif + mx[l][r] <= ANS) return;	//哪怕取一个最大值,差值都依然太小了,最优性剪枝
	if (dif + mx[l][r] < 0)				//取一个最大值,使得差值尽可能小,最优性剪枝
	{
		gmax(ANS, dif + mx[l][r]);
		return;
	}

	int sub;
	if (a[l] >= a[r])sub = a[l++];
	else sub = a[r--];

	if (clock() - ST > LIM)return;

	//<1>取l,变成dfs(l + 1, r, dif + a[l]);
	dfs(l + 1, r, dif + a[l] - sub);
	//<2>取r,变成dfs(l, r - 1, dif + a[r]);
	dfs(l, r - 1, dif + a[r] - sub);
}
int main()
{
	while(~scanf("%d", &n))
	{
		for (int i = 1; i <= n; ++i)scanf("%d", &a[i]);
		init();

		ST = clock();

		ANS = -inf;
		dfs(1, n, 0);

		if (ANS == -inf)
		{
			puts("The child will be unhappy...");
		}
		else
		{
			printf("%d\n", -ANS);
		}
	}
	return 0;
}
/*
【trick&&吐槽】
我的天,这数据水爆了啊,在0ms就可以AC了啊

【题意】
从1到n共计n(90)个物品,每个物品有一个价值a[]

小孩先手。小孩爸爸轮流做游戏。
爸爸每次可以取最左边或最右边的物品。
小孩每次选价值最大的{最左边,最右边}的物品,如果价值一样大, 则选取最左边的物品。

问你,爸爸想要输(价格严格小),而且差值尽可能少的最小差值是多少。

【分析】
首先,我们DP两个东西——
1, mx[l][r]表示对于区间[l, r],儿子先手,爸爸所能拿到的最大价值差值(差值是爸爸减儿子)
2, mn[l][r]表示对于区间[l, r],儿子先手,爸爸所能拿到的最小价值差值(差值是爸爸减儿子)

那么——
我们尝试使用搜索解决这个问题

dfs(l, r, dif)表示当前还没有取的区间范围是[l, r],儿子先手,此时爸爸减儿子的差值为dif。

那么——
1,这时先考虑剪枝——

	设置初始ANS = -inf;
	
	void dfs(int l, int r, int dif)
	{
		if (dif + mn[l][r] >= 0) return;	//哪怕取一个最小值,都会赢了儿子,是个无效状态
		if (dif + mx[l][r] <= ANS) return;	//哪怕取一个最大值,差值都依然太小了,最优性剪枝
		if (dif + mx[l][r] < 0)				//取一个最大值,使得差值尽可能小,最优性剪枝
		{
			gmax(ANS, dif + mx[l][r]);
		}
	}

2,再模拟儿子的操作,获得新的(l, r, dif)

3,接着需要进一步地考虑爸爸的操作——
	<1>取l,变成dfs(l + 1, r, dif + a[l]);
	<2>取r,变成dfs(l, r - 1, dif + a[r]);

4,考虑如何获得DP数组mn[][]和mx[][]——

mn[i][i - 1] = mx[i][i - 1] = 0;

for(int l = n; l >= 1; --l)
{
	for(int r = l; r <= n; ++r)
	{
		先模拟儿子的操作,获得新的(ll, rr)
		然后考虑父亲的操作——
		1,取ll:
			gmax(mx[l][r], a[ll] + mx[ll + 1][rr]);
			gmin(mn[l][r], a[ll] + mn[ll + 1][rr]);
		2,取rr:
			gmax(mx[l][r], a[rr] + mx[ll][rr - 1]);
			gmin(mn[l][r], a[rr] + mn[ll][rr - 1]);

	}
}

【时间复杂度&&优化】
O(0)

*/







最后排名还不错呀~

抱claris的大腿是开心呀!

这个1003明显有难以解决的数据,但是竟然听csy写一个极值的DP就0ms搜过去了…… 




吃惊。

感觉bitset过的人一样很吃惊2333。



评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值