您的位置:首页 > 移动开发

HDU 6196 happy happy happy(暴搜+剪枝+dp)

2017-10-01 17:29 411 查看
来源:http://blog.csdn.net/snowy_smile/article/details/77929954

题目:http://acm.split.hdu.edu.cn/showproblem.php?pid=6196

代码+思路:

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

int mn

, mx

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

*/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: