hdu 4876 ZCC loves cards(暴力+剪枝)

因为从n张里选k张需要C(n,k),然后k张全排列需要k!。这样果断会超时,所以需要剪枝。当选出k张后先不排序,判断每张牌选或不选共2^k种可能的组合能不能使结果更优。如果不能,就没必要在进行排列组合了。

#include<algorithm>
#include<stdio.h>
#include<cstring>
using namespace std;
int a[25],vis[205],save[25],have[25],ans[205];
int n,k,l,r;

//每张牌选或不选,共2^k种可能
void ok_calc(int no,int sum)
{
    vis[sum] = 1;
    if(no == k) return;
    ok_calc(no+1,sum^save[no]);
    ok_calc(no+1,sum);
}

//计算不连续选的话最优解
bool max_cur()
{
    memset(vis,0,sizeof vis);
    ok_calc(0,0);
    for(int i = l;i <= r;i++)
        if(!vis[i]) return false;
    return true;
}

void calc()
{
   if(!max_cur()) {return;}
   for(int i = 0;i < k;i++) have[i] = save[i]; //have用来进行全排列
   do{
        memset(ans,0,sizeof ans);
        for(int i = 1;i <= k;i++) //选几张
        {
            for(int j = 0;j < k;j++) //从第几张开始选
            {
                int tmp = 0;
                for(int kk = j;kk < j+i;kk++)
                tmp = tmp^have[kk%k];
                ans[tmp] = 1;
            }
        }
        for(int i = l;;i++)
        {
            if(!ans[i]){r = max(i-1,r);break;}
        }
   }while(next_permutation(have+1,have+k));
   //一定要从have+1开始求下一个全排列,不然会超时
   //因为是环,所以可以得到全部结果
}

//no:当前搜到第几张牌,num:一共已经选择了几张牌
void dfs(int no,int num)
{
    if(num == k) {calc();return;}
    else
    {
        for(int i = no;i < n;i++)
        {
            //保存当前选的牌
            save[num] = a[i];
            dfs(i+1,num+1);
        }
    }
}

int main()
{
    while(scanf("%d %d %d",&n,&k,&l)!=EOF)
    {
    for(int i = 0;i < n;i++) scanf("%d",&a[i]);
    sort(a,a+n);
    //初始化右端点值
    r = l - 1;
    dfs(0,0);
    if(r < l) printf("0\n");
    else printf("%d\n",r);
    }
    return 0;
}


你可能感兴趣的:(hdu 4876 ZCC loves cards(暴力+剪枝))