题目大意为:有N个牌,每个牌都有一个值,在里面选K个牌(牌之间顺序可以颠倒)围成一个圈,然后在这个圈里选择连续的几个牌的值做异或运算,要求这个圈里所能得到的最大连续序列的最大值,起始值是题目中给出的,要从它开始。
此题做法是状态压缩暴力枚举,不过纯暴力必定过不了。需要一个优化,首先确定一个事实,K个牌选出后,还需要全排列,因为牌之间顺序可以颠倒,然后选择连续的几个牌异或值,这些所有的值全部都包含在K张牌中任意几张牌异或值的里面,那么只需要2^K次就可以求出后来算出的所有值,如果这些值全部在数轴上标记后,得出的最大连续序列最大值还比之前算出的最大值少,那么就不要进行全排列展开。
这样的做法必定能过,但是效率不高,我写的要1S左右,其实还隐藏着一个优化,那K张牌不需要K!次全排列,只需要K-1次全排列就够了!,因为是围成一个圈,起点不清楚,所以可以随便定下一个起点。这种改一个字的优化可以让时间大大缩减,我写的只要187MS。
AC代码:
#include<cstdio>
#include<ctype.h>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<vector>
#include<stack>
#include<cmath>
#include<queue>
#include<set>
#include<ctime>
using namespace std;
#define NMAX 10000000
#define MOD 1000000007
int a[30],n,k,ll,ans;
bool ff[130];
int b[10],c[10];
bool flag[10];
void judge()//算出K张牌中选出连续M张全部的异或值
{
memset(ff,0,sizeof(ff));
int i,j,a1;
for(i = 0; i < k; i++)
{
a1 = 0;
j = i;
int cnt = k;
while(cnt--)
{
if(j>=k) j = 0;
a1 ^= a[c[j]];
ff[a1] = 1;
j++;
}
}
for(i = ll; ff[i] != 0; i++);
if(i - 1 > ans) ans = i-1;
}
void kkk(int w)//K张牌全排列,把第一张定好了
{
if(w == k)
{
judge();
return;
}
for(int i = 0; i < k; i++) if(!flag[i])
{
flag[i] = 1;
c[w] = b[i];
kkk(w+1);
flag[i] = 0;
}
}
int tmp[200];
void dfs(int w,int id)
{
int i,j;
if(w == k)
{
memset(ff,0,sizeof(ff));
int len = 1;
tmp[0] = 0;
for(i = 0; i < k; i++)
for(j = len-1; j >= 0; j--)
{
ff[tmp[j]^a[b[i]]] = 1;
tmp[len++] = tmp[j]^a[b[i]];
}
for(i = ll; ff[i] != 0; i++);
if(i - 1 > ans)
{
memset(flag,0,sizeof(flag));
flag[0] = 1;//定好K张牌的起点了。故只要K-1!次
c[0] = b[0];
kkk(1);
}
return;
}
for(i = id; i < n; i++)
{
b[w] = i;
dfs(w+1,i+1);
}
}
int main()
{
// freopen("input.txt","r",stdin);
// freopen("o2.txt","w",stdout);
int i,j;
while(~scanf("%d%d%d",&n,&k,&ll))
{
for(i = 0; i < n; i++) scanf("%d",&a[i]);
// if(k > n) cout<<"oh shit"<<endl;
ans = -1;
dfs(0,0);
if(ans < ll) printf("0\n");
else printf("%d\n",ans);
}
return 0;
}