[机房测试] 堆石子

Description

给定 \(n\leq 35\) 堆石子,可以把一些堆合并。合并之后,石子个数为这些堆石子个数和模 \(Mod\),求最终能得到的最大的石子个数。

Solution

最多有 \(2^n\) 种可能的石子个数,直接搜索不行。注意到背包的可合并性质,我们折半搜索。把石子分为两边,分别处理出所有可能值,这一步 \(O(2^{\frac{n}{2}})\)。然后考虑合并,两堆石子 \(0\leq a+b <2\times Mod\),对所有值排序,分类讨论,枚举 \(a\),找到最大的 \(b\),使得 \(a+b<Mod\),用这个更新答案,之后可以用 two-pointer 解决。对于 \(a+b>Mod\) 的值,最后的值是 \(a+b-Mod\) 是小于 \(Mod\) 的,所以直接贪心地取最大的 \(b\) 更新答案。

#include<stdio.h>
#include<algorithm>
using namespace std;

const int N=20;
const int M=1<<N;

int L,Mod,A[N],B[N],n,m,a[M],b[M];

void dfs(int *X,int *w,int st,int ed,int s){
    if(st==ed) w[++w[0]]=s;
    else dfs(X,w,st+1,ed,s),
         dfs(X,w,st+1,ed,(s+X[st])%Mod);
}

int main(){
    freopen("stone.in","r",stdin);
    freopen("stone.out","w",stdout);
    scanf("%d%d",&L,&Mod);
    n=L/2; m=L-n; int ans=0;
    for(int i=1;i<=n;i++) scanf("%d",&A[i]);
    for(int i=1;i<=m;i++) scanf("%d",&B[i]);
    dfs(A,a,1,n+1,0),dfs(B,b,1,m+1,0);
    sort(a+1,a+1+a[0]),sort(b+1,b+1+b[0]);
    for(int i=1;i<=b[0];i++) ans=max(ans,b[i]);
    for(int i=1;i<=a[0];i++) ans=max(ans,a[i]);
    int lt=1; while(lt<=b[0]&&a[1]+b[lt]<Mod) lt++; lt--;
    for(int i=1;i<=a[0];i++){
        while(lt&&b[lt]+a[i]>=Mod) lt--;
        ans=max(ans,a[i]+b[lt]);
        ans=max(ans,(a[i]+b[b[0]])%Mod);
    }
    printf("%d",ans);
}
posted @ 2021-11-02 21:04  Kreap  阅读(71)  评论(0编辑  收藏  举报