美妙的模拟退火
骗分还得是模拟退火
思路
了解了模拟退火算法后,本题几乎成为了一道板子题,但是还有一些细节需要去解决一下。
模拟退火算法本来是用来求最值的,所以对于本题来说其中退火的环节确实有点多余,所以我们为了本人更好地偷懒解法的正确性,我们要舍去多余的退火。
舍去了退火之后会发现,如何解决是否交换的问题呢?其实只需要全部交换就可以了,因为异或这种运算没什么太大的规律。
之后是我们总体的思路,首先预处理一下要用的
更多的代码细节还是直接看代码吧。
Code
#include<bits/stdc++.h>
using namespace std;
inline long long read()
{
long long s=0;
char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9') {
s=(s<<3)+(s<<1)+(ch^48);
ch=getchar();
}
return s;
}
inline void write(long long x)
{
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10+'0');
}
int flag=0,lg[1000005];
int a[1000005],n,m,t,pos=1,sum;
void SA(){
for(int i=1;i<=10000;i++){
int l=rand()%m+1,r=m+rand()%(n-m)+1;
sum^=a[l];
sum^=a[r];
swap(a[l],a[r]);
if(sum==pos){
flag=1;
return ;
}
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
srand(time(0));
cin>>t;
for(int i=1;i<=1000000;i++) lg[i]=lg[i-1]+(1<<lg[i-1]==i);
while(t--){
cin>>n>>m;
if(n==m){
for(int i=1;i<=n;i++) a[i]=i;
flag=sum=0;
for(int i=1;i<=m;i++) sum^=i;
pos=1;
for(int i=1;i<=lg[n];i++) pos*=2;
pos--;
if(sum==pos) {
for(int i=1;i<=n;i++) cout<<i<<" ";
cout<<"\n";
continue;
}
else {
cout<<"-1\n";
continue;
}
}
for(int i=1;i<=n;i++) a[i]=i;
flag=sum=0;
for(int i=1;i<=m;i++) sum^=i;
pos=1;
for(int i=1;i<=lg[n];i++) pos*=2;
pos--;
for(int i=1;i<=90;i++){
SA();
if(flag){
for(int j=1;j<=m;j++){
cout<<a[j]<<" ";
}
cout<<"\n";
break;
}
}
if(!flag){
cout<<"-1\n";
}
}
return 0;
}
温馨提示
本代码不一定可以一遍过,如果答案错误的话,多提交几遍就可以了。