来源:http://blog.csdn.net/snowy_smile/article/details/77929954
题目:http://acm.split.hdu.edu.cn/showproblem.php?pid=6196
代码+思路:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
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 inline void gmax(T1 &a, T2 b) { if (b > a)a = b; }
template inline void gmin(T1 &a, T2 b) { if (b < a)a = b; }
const int N = 100, M = 0, Z = 1e9 + 7, inf = 0x3f3f3f3f;
template 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)
*/