题意:给n个最多2^60的数,求其中m个的最小与(&)和
思路:
关键在于与运算的规律:一个数不断参与与运算一定是越变越小
另外要注意的并不一定前m小的数的与和最小,
例子:(二进制)1011,0011,1100
所以这题是dfs加剪枝
剪枝:1. 一边dfs一边更新ans,因为ans不可能变大
2. 当当前值与剩余数的与和作与运算的结果大于ans,说明参与与运算到最后肯定大于等于ans
注意点:一开始需要对当前值赋值,只有用数的二进制表示的位里全为1的数才不会影响后面的计算,即最大值=0x7fffffffffffffff;
代码(借鉴了别人的写法,其实用循环写dfs也行):
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<iomanip>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
using namespace std;
//A B D E K L G J
typedef long long ll;
const int MAXN = 60;
const ll INF=0x7fffffffffffffff;
ll orig[MAXN],str[MAXN];
ll ans;
int n, m;
bool cmp(ll a,ll b)
{
return a<b;
}
void dfs(int num,int pos,ll val)
{
if(ans>val)
ans=val;
if(num==m||pos==n)
return;
if((val&str[pos])>=ans)
return;
dfs(num+1,pos+1,(val&orig[pos]));
dfs(num,pos+1,val);
}
int main()
{
int T;
scanf("%d",&T);
for(int t=1;t<=T;t++)
{
scanf("%d%d",&n,&m);
//memset(str,0,sizeof str);
for (int i = 0; i < n; i++)
{
scanf("%lld", &orig[i]);
}
sort(orig,orig+n,cmp);
str[n-1]=orig[n-1];
for(int i=n-2;i>=0;i--)
str[i]=(str[i+1]&orig[i]);
ans=INF;
dfs(0,0,INF);
printf("Case #%d: %lld\n",t,ans);
}
return 0;
}