Description
给定 (nleq 35) 堆石子,可以把一些堆合并。合并之后,石子个数为这些堆石子个数和模 (Mod),求最终能得到的最大的石子个数。
Solution
最多有 (2^n) 种可能的石子个数,直接搜索不行。注意到背包的可合并性质,我们折半搜索。把石子分为两边,分别处理出所有可能值,这一步 (O(2^{frac{n}{2}}))。然后考虑合并,两堆石子 (0leq a+b <2 imes 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);
}