主要步骤是枚举C(n,k)的所有可能,然后枚举A(k,k)的所有可能,然后对每种可能进行判断
在枚举C(n,k)之后用到了一个较强的剪枝(由于数据随机,所以剪枝效果很好),就是先判断这k个数的所有组合的异或和能否超越之前求出的最优解,如果可能,再继续枚举。
#include<iostream>
#include<algorithm>
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<vector>
#include<queue>
#include<cmath>
#include<time.h>
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
int dir[4][2]={0,1,0,-1,1,0,-1,0};
int n,kk,l,ans;
int a[25];
int b[20];
int c[20];
int vis[40];
void doo()//对枚举好的排列进行处理
{
memset(vis,0,sizeof(vis));
int i,j,ct;
for(i=0,j=kk;i<kk;i++,j++)
b[j]=b[i];
c[0]=b[0];
for(i=0;i<kk*2;i++)
c[i]=c[i-1]^b[i];
for(i=1;i<=kk;i++)
{
for(j=0;j<kk;j++)
{
if(j==0)
{
ct=c[i+j-1]-l;
if(ct<0||ct>=kk*kk)continue;
vis[ct]=1;
}
else
{
ct=(c[i+j-1]^c[i-1])-l;
if(ct<0||ct>=kk*kk)continue;
vis[ct]=1;
}
}
}
int sum=-1;
for(i=0;i<kk*kk;i++)
{
if(vis[i]==0)
break;
else
sum=i;
}
if(sum>ans)
ans=sum;
}
void dfs(int temp)//枚举排列
{
if(temp==kk-1)
{
doo();
return ;
}
for(int i=temp;i<kk-1;i++)
{
swap(b[temp],b[i]);
dfs(temp+1);
}
}
void solve(int temp)
{
int i,j,k=0;
int sum;
for(i=0;i<n;i++)
if((temp&(1<<i))!=0)
b[k++]=a[i];
memset(vis,0,sizeof(vis));
for(i=1;i<(1<<kk);i++)//剪枝:判断当前情况最优时能否达到最优解
{
sum=0;
for(j=0;j<kk;j++)
{
if((i&(1<<j))!=0)
sum=sum^b[j];
}
sum=sum-l;
if(sum<0||sum>=kk*kk)continue;
vis[sum]=1;
}
sum=-1;
for(i=0;i<kk*kk;i++)
{
if(vis[i]==0)
break;
else
sum=i;
}
if(sum>ans)
dfs(0);//如果可能达到最优继续进行排列的枚举和处理
}
int main()
{
srand((int)time(0));
while(scanf("%d%d%d",&n,&kk,&l)!=EOF)
{
int i,j,k;
ans=-1;
for(i=0;i<n;i++)
scanf("%d",&a[i]);
k=kk;
int comb = (1<<k) - 1;
while(comb < 1<<(n))
{
solve(comb);//位运算枚举出来C(n,kk)后进行处理
int x = comb & -comb, y = comb + x;
comb = ((comb & ~y) / x >> 1) | y;
}
if(ans==-1)
printf("0\n");
else
printf("%d\n",ans+l);
}
return 0;
}