D - happy happy happy HDU - 6196(剪枝+中途相遇+搜索)

C - happy happy happy

 HDU - 6196

https://cn.vjudge.net/contest/250629#problem/C(比赛网址)

https://cn.vjudge.net/problem/HDU-6196(题目网址)

题意:Bob正在和一个孩子一起玩耍,一排有n个数字,每个人每次只能从最左边或者shi最右边开始拿一个数,Bob知道孩子每次都会拿左右两边比较大的一个,如果两个数字一样大,Bob会优先拿左边的,现在Bob想让孩子拿的数字之和比自己的大并且自己与孩子的差值尽量小,输出两个人的最小差值,如果不管怎么拿孩子拿的数字都比Bob的小输出“The child will be unhappy...”

思路:这个题中n最大是90,我们可以暴力找答案,普通的没有优化的搜索肯定是会超时的。加上剪枝,中途相遇等方法来优化我们的程序。

剪枝:我们定义两个数组  mi[l][r]:Bob拿的数字与孩子拿的数字之和最小的差值(Bob拿的数字之和-孩子拿的数字之和)

                                         ma[l][r]:Bob拿的数字之和与孩子拿的数字之和最大的差值(Bob拿的数字之和-孩子拿的数字之和)

            在dfs(int l,int r,int cha)代表当前要拿的区间是l~r,差值是cha(Bob的数字之和减去孩子的数字之和)

           当cha+mi[l][r]>=0时,剪枝,如果当前的差值加上区间l~r最小差值都大于0,肯定找不到答案

          当cha+ma[l][r]<=ans,剪枝,ans代表我们当前搜到的最优解,如果当前的差值加上区间l~r的最大差值还小于最优解,在搜肯定找不到比当前答案更优的情况,剪枝。

        如果cha+ma[l][r]<0&&cha+ma[l][r]>ans,这时我们更新最优值。
ma[l][r],mi[l][r]的值我们要预先处理,区间在进行初始化的时候要注意,不是所有区间都要初始化,

中途相遇:我们预先处理每个区间内所有差值的情况,当搜到l~r区间,我们可以直接查找与-cha最相近的值。这样也会很省时间。

在预先处理区间内所有答案的方法很巧妙,用二进制枚举的方法找出所有情况 ,区间的长度选取要注意,区间选择的不合适也会造成超时。

之后再搜索的时候我们直接查找就可以了

it=lower_bound(v[l].begin(),v[l].end(),-cha);

 

#include <iostream>
#include <cstring>
#include <algorithm>
#include <string>
#include <vector>
#include <set>
#include <map>
#include <cstdio>
using namespace std;
const int maxn=90+10;
vector<int> v[maxn];
int dis;
map<int,int> mat;
const int inf=0x3f3f3f3f;
vector<int> ::iterator it;
int mi[maxn][maxn];
int ma[maxn][maxn];
int n;
int a[maxn];
int ans;
void Init()
{
    ///
    for(int i=0; i<maxn; i++)///
    {
        for(int j=i; j<maxn; j++)
        {
            mi[i][j]=(int)inf;
            ma[i][j]=(int)-inf;
        }

        v[i].clear();
    }
    for(int i=n-1; i>=0; i--)
    {
        for(int j=i; j<n; j++)
        {
            int sub;
            int l=i;
            int r=j;
            if(a[l]>=a[r])
                sub=a[l++];
            else
                sub=a[r--];
            mi[i][j]=min(mi[i][j],mi[l+1][r]+a[l]-sub);///选左边
            mi[i][j]=min(mi[i][j],mi[l][r-1]+a[r]-sub);///选右边
            ma[i][j]=max(ma[i][j],ma[l+1][r]+a[l]-sub);///选左边
            ma[i][j]=max(ma[i][j],ma[l][r-1]+a[r]-sub);///选右边
        }

    }
}
void dfs(int l,int r,int cha)
{
    if(l>r)
    {
        if(cha<0)
        {
            ans=max(ans,cha);
        }
    }

    /// 剪枝
    if(cha+mi[l][r]>=0)
        return ;
    if(cha+ma[l][r]<ans)
        return ;
    if(cha+ma[l][r]<0)
    {
        ans=max(ans,cha+ma[l][r]);
        return;
    }

    ///中途相遇
    if(r-l+1==2*dis)
    {
        it=lower_bound(v[l].begin(),v[l].end(),-cha);
        int temp=*it;
        if(it==v[l].end()||temp==-cha)
        {
            if(it==v[l].begin())
                return ;
            else
                it--;
        }
        if(cha+temp<0)
        {
            ans=max(cha+temp,ans);
        }
        return ;
    }

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

}
int main()
{

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

        for(int i=0; i<n; i++)
        {
            scanf("%d",&a[i]);
        }
        Init();
        ///
        if(n==2)
        {
            if(a[0]==a[1])
                printf("The child will be unhappy...\n");
            else
                printf("%d\n",max(a[0],a[1])-min(a[0],a[1]));
            continue;
        }
        ///
        dis=min(n/4,16);
        ans=-inf;

        for(int i=0; i+dis*2-1<n; i++)
        {
            mat.clear();
            int l=i;
            int r=i+dis*2-1;
            for(int s=0; s<(1<<dis); s++)
            {
                l=i;
                r=i+dis*2-1;
                int sum=0;
                for(int j=0; j<dis; j++)
                {
                    if(a[l]>=a[r])
                        sum-=a[l++];
                    else
                        sum-=a[r--];
                    if(!(s&(1<<j)))
                        sum+=a[l++];
                    else
                        sum+=a[r--];
                }
                if(!mat[sum])
                {
                    mat[sum]=1;
                    v[i].push_back(sum);
                }
            }
            sort(v[i].begin(),v[i].end());
        }
        dfs(0,n-1,0);
        if(ans!=-inf)
            printf("%d\n",-ans);
        else
            printf("The child will be unhappy...\n");
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值