题意:给你n(n<=50)个数,m( m<=1e5 )次询问,每次询问给你三个数,问在给出的数里面存不存在任意取10个(不包含这三个数)的数的和刚好为87,每次询问输出Yes/No。
1)bitset?
C++中可以直接用的一个类。
bitset<num> name;
可以看作一个大小为num的数组,里面的元素只能是0或1,有趣的是它能进行和数组一样的操作又能把它当做一个整体(当做一个二进制数)来进行位运算操作。
1个应用:当做一个数的集合,可以用一个name<<a对集合中的所有元素+a操作(若集合为空,则操作无效),用|符实现两个集合的取交集。
例如:name1 : 0000010010001001(包含0,3,7,10四个元素)
name1<<2 :0001001000100100(包含2,5,9,12四个元素)
name1|name1<<2 :0001011010101101(包含0,3,7,10,2,5,9,12 八个元素,相当于只用了两个位操作就得到了对该集合所有元素加上2/不加上2 的所有结果)
2)本题分析
dp分析即可,Bitset[10](使用10个数)由Bitset[9]获得。初始化所有元素为空,但要设置一个含0元素的集合,具体见代码。
分析下时间复杂度:50*50*50*50*10=6.25e7 (超时的边缘)
//如果用01背包来做这题,dp[10][87][0/1],时间复杂度:6.25e7*87=5.4375e9
上代码,交了好几次,基本都卡过去了,但最好也只是902ms(在G++上交)
#include<stdio.h>
#include<stdlib.h>
#include<iostream>
#include<math.h>
#include<queue>
#include<string.h>
#include<algorithm>
#include<bitset>
#define PI acos(-1.0)
using namespace std;
int n,a[60];
bool reco[60][60][60][2]; //记录该3个数是否计算过,是否yes
bool check(int x,int y,int z)
{
if(reco[x][y][z][0]==1) return reco[x][y][z][1];
bitset<90> bs[12];
bs[0][0]=1;
for(int i=1;i<=n;i++)
{ if(*(a+i)>87) continue;
if(i==x||i==y||i==z) continue;
for(int j=10;j>=1;j--)
*(bs+j)|=*(bs+j-1)<<*(a+i);
if(bs[10][87]==1) break;
}
reco[x][y][z][1]=(bs[10][87]==1);
reco[x][y][z][0]=1;
return reco[x][y][z][1];
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
memset(reco,0,sizeof(reco));
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
int q;
scanf("%d",&q);
while(q--)
{
int x[5];
scanf("%d%d%d",x,x+1,x+2);
sort(x,x+3);
if(reco[x[0]][x[1]][x[2]][1]) printf("Yes
");
else if(check(x[0],x[1],x[2])) printf("Yes
");
else printf("No
");
}
}
}
以及AtCoder Grand Contest #020 by tourist C题 (单纯bitset)
链接:https://agc020.contest.atcoder.jp/tasks/agc020_c
AC代码:
#include<stdio.h>
#include<stdlib.h>
#include<iostream>
#include<math.h>
#include<queue>
#include<string.h>
#include<bitset>
#define PI acos(-1.0)
using namespace std;
int main()
{
bitset<4000005> bs(1);
int n,x,sum=0;
scanf("%d",&n);
for(int i=0;i<n;i++)
{
scanf("%d",&x);
bs|=bs<<x;
sum+=x;
}
if(n==1)
{
printf("%d
",sum);
return 0;
}
for(int i=(double)sum/2.0+0.5;i<=sum;i++)
{
if(bs[i]==1)
{
printf("%d
",i);
break;
}
}
return 0;
}