[日常训练] 买汽水

来源:互联网 发布:苹果root软件 编辑:程序博客网 时间:2024/05/17 01:11

【问题描述】

czyz暑期集训一共N天。由于jmy和lkf玩游戏总是输,作为惩罚,他需要给oiers买汽水。
jmy最多只能给大家花M元钱。由于每天汽水的价格都不固定,现在给出每天买汽水的花销,我们可以随意选择让jmy哪些天买汽水(当然总花费不能超过M)。请问最多一共能够花掉jmy多少钱呢?
暑假最多不超过40天,jmy给大家花的钱最多有一亿。

【输入格式】

输入第一行有两个整数N,M。 1<=N<=40,0<=M<=100000000。
接下来一行有N个数,第i个数表示第i天的汽水花销。每天汽水的花销p<=100000000。

【输出格式】

输出一行一个整数,表示我们最多能够花掉jmy多少钱。

【输入输出样例一】

drink.in
3 10
1 2 3

drink.out
6

【输入输出样例二】

drink.in
4 10
4 3 5 11

drink.out
9

【数据规模】

对于10%的数据,N<=10。
对于30%的数据,N<=20。
对于60%的数据,N<=30。
对于100%的数据,N<=40。

【10分】 01背包

【60分】 深搜 + 剪枝

直接暴力深搜,考虑如何剪枝:
[1]、将花销数组从大到小排序,这样在搜索时会优先选取花销大的搜索,加快效率。
[2]、记录上一次选取的位置lst,则这次只要在lst+1~n位置进行搜索即可。
[3]、记录花销数组的后缀和,若此时将未搜索到的所有位置的花销全部计入总花销若未超出容量M,则可直接用于更新答案;若仍不如当前的最优方案,则该情况可以直接排除。
[4]、记录最小的单天花销mx,若剩余的容量小于mx,则该情况可以直接排除。

【代码】

#include <iostream>#include <algorithm>#include <cstdio> using namespace std;const int N = 45;int a[N], c[N], n, m, Ans, mx = 0x3f3f3f3f; bool vis[N];void Dfs(const int &k, const int &lst, const int &w){    int tmp = w + c[lst + 1];    if (tmp <= Ans || w > m) return;    if (tmp <= m && tmp >= Ans) return (void)(Ans = tmp);      if (w > Ans) Ans = w;    if (k > n || m - w < mx) return ;    for (int i = lst + 1; i <= n; ++i)     Dfs(k + 1, i, w + a[i]);}inline bool cmp(const int &x, const int &y){    return x > y;   }int main(){    freopen("drink.in", "r", stdin);    freopen("drink.out", "w", stdout);    scanf("%d%d", &n, &m);    for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);    sort(a + 1, a + n + 1, cmp);    for (int i = n; i >= 1; --i)     {        c[i] = c[i + 1] + a[i];        if (mx > c[i]) mx = c[i];      }    Dfs(1, 0, 0);    printf("%d\n", Ans);    fclose(stdin); fclose(stdout);    return 0;}

【100分】 折半搜索

最大数据N40,我们可以考虑将其分成两部分搜索,用01表示某一天是否买汽水,将两个部分所有的01状态全部搜索出来,总共也就是2×220种状态。两个部分每种状态所产生的花销分别计入两个数组c,d,那么答案即为Max(c[i]+d[j])
考虑怎样O(n)求出Max(c[i]+d[j]):将这两个数组分别按从小到大排序,记两个指针l,r,初始分别指向c数组的起始和d数组的末尾。每次将l右移,则若c[l]+d[r]>m时,r左移,否则不动,则合法方案c[l]+d[r]即可用于更新答案。

【代码】

#include <iostream>#include <cstdio>#include <algorithm>using namespace std;const int N = 45, M = (1 << 20) + 5;int a[N], b[N], c[N], d[N];int Ans, n, x, m;inline void Dfs1(const int &k, const int &w){    if (w > m) return ;    if (k > a[0]) return (void)(c[++c[0]] = w);    for (int i = 0; i <= 1; ++i)     Dfs1(k + 1, w + (i ? a[k] : 0));}inline void Dfs2(const int &k, const int &w){    if (w > m) return ;    if (k > b[0]) return (void)(d[++d[0]] = w);    for (int i = 0; i <= 1; ++i)      Dfs2(k + 1, w + (i ? b[k] : 0));}inline void CkMax(int &x, const int &y) {if (x < y) x = y;}int main(){    freopen("drink.in", "r", stdin);    freopen("drink.out", "w", stdout);     scanf("%d%d", &n, &m);    for (int i = 1; i <= n; ++i)     {        scanf("%d", &x);        if (i & 1) a[++a[0]] = x;         else b[++b[0]] = x;     }    Dfs1(1, 0); Dfs2(1, 0);    int l = 0, r = d[0];    sort(c + 1, c + c[0] + 1);    sort(d + 1, d + d[0] + 1);    while ((++l) <= c[0] && r)    {        while (c[l] + d[r] > m) r--;        if (r) CkMax(Ans, c[l] + d[r]);     }    printf("%d\n", Ans);    fclose(stdin); fclose(stdout);}
原创粉丝点击