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

来源: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*/

你可能感兴趣的:(搜索,动态规划)