天平

Posted on 2019-02-03 09:48  Mocking_Jimmy  阅读(156)  评论(0编辑  收藏  举报

1月26日

Description

FJ有一架用来称牛的体重的天平。与之配套的是N \((1<=N<=40)\) 个已知质量的砝码(所有砝码质量的数值都在31位二进制内)。每次称牛时,他都把某头奶牛安置在天平的某一边,然后往天平另一边加砝码,直到天平平衡,于是此时砝码的总质量就是牛的质量(FJ不能把砝码放到奶牛的那边,因为奶牛不喜欢称体重,每当FJ把砝码放到她的蹄子底下,她就会尝试把砝码踢到FJ脸上)。天平能承受的物体的质量不是无限的,当天平某一边物体的质量大于 \(C(1<=C<2^{30})\) 时,天平就会被损坏。

砝码按照它们质量的大小被排成一行。并且,这一行中从第3个砝码开始,每个砝码的质量至少等于前面两个砝码(也就是质量比它小的砝码中质量最大的两个)的质量的和。

FJ想知道,用他所拥有的这些砝码以及这架天平,能称出的质量最大是多少。由于天平的最大承重能力为C,他不能把所有砝码都放到天平上。

现在FJ告诉你每个砝码的质量,以及天平能承受的最大质量。你的任务是选出一些砝码,使它们的质量和在不压坏天平的前提下是所有组合中最大的。

Input

第1行: 两个用空格隔开的正整数,N和C。
第2..N+1行: 每一行仅包含一个正整数,即某个砝码的质量。保证这些砝码的质量是一个不下降序列。

Output

第1行: 一个正整数,表示用所给的砝码能称出的不压坏天平的最大质量。

Sample Input

3 15
1
10
20

Sample Output

11

Solution

  • 【我的方法】

    我在比赛时直接bfs暴搜,于是50分滚蛋。。。

  • 【国外大佬】

The brute force approach tries every possible combination of weights, adds them up, and checks against the best current combination and the upper bound. For 40 weights, there are 240 sets - roughly 1012, which is far too slow. The unusual property of the weights (that \(w[i+2] >= w[i+1] + w[i]\) ) allows one to speed up the algorithm. If we write

\[w[3] - w[2] >= w[1] \\ w[4] - w[3] >= w[2] \\ \vdots\\ w[i+2] - w[i+1] >= w[i] \]

and add up the equations, we obtain \(w[i+2] - w[2] >= \sum w_j\) . In other words,using the first i weights will never give as much as just using weight i+2. Now consider the following cases:

\(w[N-1] + w[N] <= C\): If we take everything except w[N], we get less than \(w[N-1] + w[N]\), in which case we might as well just take w[N-1] and w[N] instead. So either way, we must take w[N].

\(w[N] <= C < w[N-1] + w[N]\): We cannot take both w[N-1] and w[N]. If we take neither then we get less than w[N] and should just take w[N] instead. So we must take exactly one of them.

\(C < w[N]\) : obviously we cannot take w[N].

In the first and third cases, we know whether to take w[N], and repeat the problem using only the first N - 1 weights and any remaining capacity. In the second case, we have to branch and consider both cases, in each case solving the problem for the remaining N - 2 weights. In the worst case we will always have the second case, causing us to branch two ways at each level. However, there are only N/2 levels of recursion (because we eliminate two, rather than one, weight each time), so the running time is proportional to 2N/2 rather than 2N.


\(by\) Bruce Merry

咳咳,我模拟了一下。

#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;

int n,c,maxn,sum,w[42];

void dfs(int x)
{
    if(x==0)
    {
        if(sum<=c)
    	    maxn=max(maxn,sum);
        return;
    }
    if(c-sum>=w[x]+w[x-1])
    {
        sum+=w[x];
        dfs(x-1);
        sum-=w[x];
    }
    else if(c-sum<w[x])
        dfs(x-1);
    else
    {
        sum+=w[x];
        dfs(x-2);
        sum-=w[x];
        sum+=w[x-1];
        dfs(x-2);
        sum-=w[x-1];
    }
}
int main()
{
	scanf("%d%d",&n,&c);
	for(int i=1;i<=n;i++)
		scanf("%d",&w[i]);
	dfs(n);
	printf("%d",maxn);
	return 0;
}
  • 【国内大佬】

国人的思路就十分明了。既然 \(2^{40}\) 的时空复杂度都过不了(时->暴搜,空->背包)。那就把他拆成两段来做。

首先第一段 \(2^{20}\) 直接暴搜然后把每一个答案记录下来,再排个序。第二段每一个在上一个数组里二分一下,满足 \(sum+ans_i\leq c\) 就把 ans[i] 加上 x 就OK了。

调了一个小时没调出来,放弃