「这是我参与2022首次更文挑战的第9天,活动详情查看:2022首次更文挑战」。
这是蓝桥杯刷题训练里面的一道题,主要包含了状态搜索(也有其他的做法)。
题目
给定一些数,每次只能对这些数中的一个数操作,将这个数分到A组或者B组,每次操作结束后,要求A组和B组之间的差不超过一个数r,直到最后这些数全部分完。最后分别从小到大输出A组和B组。并且要求第一个数必须分到A组。 输入n,表示n个数,输入r表示不能超过的那个数。 再输入n个数。
输出是输出两个组的数。
思路
我们一般的想法可能是一个数一个数的找,而由于每次操作完一个数后都要比较一次,所以这种搜索的效率实在是比较低,而且算法的设计也不好设计。我们不妨换个思路,先找到最终结果的所有情况。对这些情况一个个去遍历,然后去判断,观察是否符合题干要求,如果符合的话就可以直接输出了。因为这道题最终是一定有解的,从所有的情况中一个个去找,一定可以找到答案的。而如何存储所有的情况呢,这里就可以用到状态压缩了,将这些数用独一无二的二进制数来表示,遍历二进制数的时候,是1的位子就分到A组,是0的位子就分到B组。题干中还有一个要求是第一个数必须分到A组,那么我们提前把第一个数选出分给A组即可,状态搜索的流程还是一样的。
代码
#include<bits/stdc++.h>
using namespace std;
int N[100086],val1[100086],val2[100086];
int n,r,idx1,idx2;
bool check(){
int x=0,y=0,sum1=0,sum2=0,flag=1;
sum1=val1[++x];
while(abs(sum1-sum2)<=r&&flag){
flag=0;
if(abs(sum1+val1[x+1]-sum2)<=r&&x+1<=idx1){
x++;
flag=1;
sum1+=val1[x];
}
if(abs(sum1-val2[y+1]-sum2)<=r&&y+1<=idx2){
y++;
flag=1;
sum2+=val2[y];
}
}
if(x==idx1&&y==idx2){
return true;
}
return false;
}
int main(){
cin>>n>>r;
for(int i=0;i<n;i++){
cin>>N[i];
}
int first=N[0];
sort(N,N+n);
for(int i=1;i<(1<<n)-1;i++){//这里去掉两种情况,就是全部在一个组的情况。这种情况肯定是不出在的
int judge=0;
idx1=idx2=0;
memset(val1,0,sizeof(val1));
memset(val2,0,sizeof(val2));
for(int j=0;j<n;j++){
if((1<<j)&i){
val1[++idx1]=N[j];
}
else{
val2[++idx2]=N[j];
}
}
if(check()){
for(int j=1;j<=idx1;j++){
if(val1[j]==first)//第一个数在哪,谁就是A组
judge=1;
}
if(judge){
for(int j=1;j<=idx1;j++){
cout<<val1[j]<<' ';
}
cout<<endl;
for(int j=1;j<=idx2;j++){
cout<<val2[j]<<' ';
}
cout<<endl;
}
else{
for(int j=1;j<=idx2;j++){
cout<<val2[j]<<' ';
}
cout<<endl;
for(int j=1;j<=idx1;j++){
cout<<val1[j]<<' ';
}
cout<<endl;
}
break;
}
}
return 0;
}