HDU 6196 happy happy happy [折半暴搜+剪枝+DP]

25 篇文章 0 订阅
2 篇文章 0 订阅

题意:给你长度为n的序列,爸爸和儿子玩一个游戏,儿子先手,儿子每次都选择最左边与最右边最大的那个拿走(若左右相等拿左边),爸爸可以任意拿最左边或者最右边。

题解:

①对于这道题n只有90,我们可以先折半,将前半段与后半段分开考虑,预处理后半段暴力结果,然后dfs(l,r,cha)(表示当前搜到区间[l,r]时候的差值)暴搜前半段情况,对于搜索到区间大小等于后半段操作长度的时候,我们对于当前差值cha在后半段预处理的结果中lower_bound最优答案。我这里选择后半段长度为32,(再长就T了= =)。

②由于前半段完全暴搜肯定爆时间,于是我们加入剪枝。

首先我们要预处理所有区间能得到的最大值差值与最小差值。之后

剪枝:①对于当前区间[l,r],假如当前差值+mindif[l,r]>=0,说明继续搜下去不可能有解,直接return;

   ②对于当前区间[l,r],假如当前差值+maxdif[l,r]<=ans,那说明继续做下去得到的解肯定比ans小,差值更大,也直接return;

   ③对于当前区间[l,r],假如当前差值+maxdif[l,r]<0,那我们直接可以更新答案,然后return;

③那接下来我们如何得到所有区间的最值呢,我们考虑dp

枚举所有区间,先将儿子的选择消去。然后考虑爸爸的转移(ll,rr为转移之后的l与r):

mindif[l][r]=min(mindif[l][r],a[ll]+mindif[ll+1][rr]-sub);
mindif[l][r]=min(mindif[l][r],a[rr]+mindif[ll][rr-1]-sub);
maxdif[l][r]=max(maxdif[l][r],a[ll]+maxdif[ll+1][rr]-sub);

maxdif[l][r]=max(maxdif[l][r],a[rr]+maxdif[ll][rr-1]-sub);

之后我们就可以得到答案了

AC代码:

#include<stdio.h>
#include<vector>
#include<string.h>
#include<algorithm>
#include<map>
#include<time.h>
#define N 95
using namespace std;
int a[N];
vector<int>vt[N];
vector<int>::iterator it;
int mi[N][N],ma[N][N];
int ans,n,hou;
map<int,int>mp;
void init()  
{  
    for(int i=0;i<N;i++)
		for(int j=i;j<N;j++)
		{
			mi[i][j]=(int)1e9;
			ma[i][j]=(int)-1e9;
		} 
    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--];  
            ma[l][r]=max(ma[l][r],a[ll]+ma[ll+1][rr]-sub);  
            mi[l][r]=min(mi[l][r],a[ll]+mi[ll+1][rr]-sub);  
            ma[l][r]=max(ma[l][r],a[rr]+ma[ll][rr-1]-sub);  
            mi[l][r]=min(mi[l][r],a[rr]+mi[ll][rr-1]-sub);  
        }  
    }  
}  
void dfs(int l,int r,int cha)
{
	if(r-l+1==hou*2)
	{
		it=lower_bound(vt[l].begin(),vt[l].end(),-cha);
		if(it==vt[l].end()||*it==-cha)
		{
			if(it==vt[l].begin())return ;
			else it--;
		}
		if(cha+*it<0)ans=max(ans,cha+*it);
		return ;
	}
	if(mi[l][r]+cha>=0)return ;
	if(ma[l][r]+cha<=ans)return ;
	if(ma[l][r]+cha<0)
	{
		ans=max(ans,ma[l][r]+cha);
		return ;
	}
	int sub;
	if(a[l]>=a[r])sub=a[l++];  
    else sub=a[r--];
    dfs(l+1,r,cha+a[l]-sub);
    dfs(l,r-1,cha+a[r]-sub);
}
int main()
{
    int t=clock();
    while(~scanf("%d",&n))
    {
        for(int i=0;i<N;i++)vt[i].clear();
        for(int i=1;i<=n;i++)
            scanf("%d",&a[i]);
        if(n==2)
        {
        	if(a[1]==a[2])printf("The child will be unhappy...\n");
        	else printf("%d\n",max(a[1],a[2])-min(a[1],a[2]));
			continue ;
        }
        init();
        hou=min(16,n/4),ans=(int)-1e9;
        for(int i=1;i+hou*2-1<=n;i++)
        {
        	mp.clear();
			for(int state=0;state<(1<<hou);state++)
	        {
	            int sum=0,l=i,r=l+2*hou-1;
	            for(int j=0;j<hou;j++)
	            {
	                if(a[l]>=a[r])sum-=a[l++];
	                else sum-=a[r--];
	                if(!(state&(1<<j)))sum+=a[l++];
	                else sum+=a[r--];
	            }
	            if(mp[sum]==0)
	            {
					vt[i].push_back(sum);
					mp[sum]=1;
	            }
	        }
	        sort(vt[i].begin(),vt[i].end());
        }
        dfs(1,n,0);
        if(ans==(int)-1e9)printf("The child will be unhappy...\n");
        else printf("%d\n",-ans);
    }
}





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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值