大奖赛 解题报告

据说是教练从清北食堂捞的题目

大奖赛

题目背景

你不努力,未来别人壁咚的墙都是你砌的!

题目描述

\(Lancelot\)市最近要举办大奖赛拉!住在市里的市民都十分兴奋,\(Morgan\)也不例外。他查了一下比赛的信息,发现比赛一共有\(N\)场,并且每一场的门票价格可能会不相等。\(Morgan\)留给比赛的预算是\(K\)元;他想知道,一共有多少种买票的方案,使得门票之和不超过\(K\)呢?

输入输出格式

输入格式:

第一行两个整数\(N\)\(K\),代表比赛的场数和自己的预算。 第二行\(N\)个整数\(A_i\),代表每场比赛的门票价格。

输出格式:

一行一个整数,代表买票的总方案数。

说明

对于30%的数据,N<=10
对于60%的数据,K<=10000
对于100%的数据,1<=N<=40,0<k,Ai<=1000000000。


30% \(O(2^N)\) 暴搜即可

50% \(O(NK)\) 0/1背包

100% \(O(2^{N/2}log2^{N/2})\) 双向搜索

介绍一下双向搜索的方法。

将门票价格分成平均大小的两个集合,分别对两个集合进行枚举并统计答案。

具体实现如下,将其中一个集合所产生的状态用数组存储并排序,当枚举另一个集合得到答案\(cost\)时,在第一个集合产生的答案中进行二分查找(查找\(k-cost\)在之中的前驱位置),得到的位置即为这个\(cost\)产生的答案。

这本质上其实是一种拿空间换时间的方法,把一半所产生的状态进行有序存储,在另一半产生答案时直接二分查找统计。

可以做做这个题,差不多,送礼物

值得一提的是,这个题我也有一点收获,是关于搜索的。

在枚举状态时,如果我们这么枚举

void dfs1(int now,ll cost)
{
    if(cost>k) return;
    if(now==mid+1)
    {
        costa[++cnt]=cost;
        return;
    }
    dfs1(now+1,cost+a[i]);
    dfs2(now+1,cost);
}

那么产生的搜索树是这样的

左右子树分别表示这个点选没选

其中,每个粉红色的点所在的链表示一个状态,也就是说,只有叶子节点可以代表状态。这和平常的思路是比较相近的。

但如果这样写

void dfs1(int now,ll cost)
{
    costa[++cnt]=cost;
    for(int i=now+1;i<=mid;i++)
        if(cost+a[i]<=k)
            dfs1(i,cost+a[i]);
}

那么产生的搜索树是这样的

节点标号代表选中的节点在a数组的位置。

这样,每一个节点所代表的链都是一个状态了。

显然,第二种是比较优秀的。


code:

#include <cstdio>
#include <algorithm>
#define ll long long
const int N=45;
ll a[N],n,k,costa[1<<21],cnt=0,mid,ans=0;
void dfs1(int now,ll cost)
{
    costa[++cnt]=cost;
    for(int i=now+1;i<=mid;i++)
        if(cost+a[i]<=k)
            dfs1(i,cost+a[i]);
}
void find(ll x)
{
    ll l=1,r=cnt;
    while(l<r)
    {
        ll Mid=l+r+1>>1;
        if(costa[Mid]<=x)
            l=Mid;
        else
            r=Mid-1;
    }
    ans+=l;
}
void dfs2(int now,ll cost)
{
    find(k-cost);
    for(int i=now+1;i<=n;i++)
        if(cost+a[i]<=k)
            dfs2(i,cost+a[i]);
}
int main()
{
    scanf("%lld%lld",&n,&k);
    for(int i=1;i<=n;i++)
        scanf("%lld",a+i);
    mid=n>>1;
    std::sort(a+1,a+1+n);
    dfs1(0,0);
    std::sort(costa+1,costa+1+cnt);
    costa[0]=-1;
    dfs2(mid,0);
    printf("%lld\n",ans);
    return 0;
}

2018.6.21

猜你喜欢

转载自www.cnblogs.com/ppprseter/p/9209818.html
今日推荐