题目链接:http://codeforces.com/problemset/problem/550/B
【题目大意】
每组数据输入 n,l,r,x
然后输入n个数
从n个数中选出 m个 数 要求这m个数的和大于等于 l ,并且小于等于 r , 而且这m个数中 最大值 和最小值的差 不小于x
求有多少种选法。
【思路】
由于 n 的范围是 1~15 ,应该可以用暴力解决,,但是写不出来。。
后来学长讲了一个二进制状态压缩的方法,
一共n个数,我们把每个数看做一个开关,那么所有的可能一共有 2^n种 ,
从1 到2 ^n 对应了所有的可能 ,
如何判断这n个数中,哪个是选中的哪?
2^0 = 00001
2^1 = 00010
2^2 = 00100
2^3 = 01000
我们只要把每种可能 与 2的0 ~n 次方进行 位与操作 就可以判断了
这样全部枚举就能得出结果, n最大为15, 2^15小于 1e6 所以不会超时
【源代码】
#include <iostream>
#include <cstdio>
using namespace std;
int num[20];
int tmp[20];
const int INF = 0xfffffff;
int main(){
int n,l,r,x;
for(int i=1;i<=15;i++)
tmp[i]=(1<<(i-1));
while(scanf("%d%d%d%d",&n,&l,&r,&x)!=EOF){
int count=0;
for(int i=1;i<=n;i++) scanf("%d",&num[i]);
for(int i=1;i<=(1<<n);i++){
int maxx=-1,minn=INF,sum=0;
for(int j=1;j<=15;j++){
if(i&tmp[j]){
sum+=num[j];
maxx=max(maxx,num[j]);
minn=min(minn,num[j]);
}
}
if(sum>=l&&sum<=r&&maxx-minn>=x){
count++;
}
}
cout<<count<<endl;
}
return 0;
}
【补充一个搜索版本的】
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int n,l,r,x;
int num[20];
bool vis[20];
int maxx , minn;
int sum;
int ans;
void dfs(int po){
if(sum>=l && sum<=r && maxx-minn>=x){
ans++;
// cout<<sum<<" "<<maxx<<" "<<minn<<endl;
}
for(int i=po;i<n;i++){
if(!vis[i]){
if(sum+num[i]<=r){
int premax = maxx;
if(num[i]>maxx){
maxx = num[i];
}
int premin = minn;
if(num[i]<minn)
minn = num[i];
sum+=num[i];
vis[i] = true;
dfs(i); //回溯时,连当前的最大最小值也要回溯
sum-=num[i];
vis[i] = false;
maxx = premax; //用premax来记录
minn = premin;
}
}
}
}
int main(){
while(scanf("%d%d%d%d",&n,&l,&r,&x)!=EOF){
memset(vis,0,sizeof(vis));
sum = 0;
ans = 0;
for(int i=0;i<n;i++){
scanf("%d",&num[i]);
}
maxx = -1;
minn = 2000000000;
if(n == 1 && l <= num[0] && r >= num[0] && x == 0){ //加了一个特判
printf("1\n");
}
else{
dfs(0); //从第一个数开始搜索
printf("%d\n",ans);
}
}
return 0;
}