题解:CF1238E Keyboard Purchase
简要题意:
给定一个长度为
n 的字符串s ,s 中的每一个字符都是前m 个小写字母中的一个。你需要找到一个长度为m 的字符串t ,满足前m 个小写字母都刚好在t 中出现一次,令字符j 在t 中的位置为pos_j 。然后求出下面这个式子的最小值:
\sum_{i=2}^n | pos_{s_i} - pos_{s_{i-1}}|
读完题就意识到如果已知
每一次交换两个随机位置,再根据概率确定应不应该接受劣解。对于这道题,降温系数调低一点更容易过。
代码如下:
//code by ElderFour
//problem:CF1238E
#include<bits/stdc++.h>
using namespace std;
const double T0=2000.0,TD=0.98,Tk=1e-15;
const long long N=100010,M=25,INF=1e18;
long long n,m;
long long cnt[M][M],dis[M][M];
long long tmp;
string st;
long long a[N];
long long b[M];
long long calculate()
{
for(int i=1;i<=m;i++)
{
for(int j=i+1;j<=m;j++)
{
dis[min(b[i],b[j])][max(b[i],b[j])]=j-i;
}
}
long long sum=0;
for(int i=1;i<=m;i++)
{
for(int j=i+1;j<=m;j++)
{
if(!cnt[i][j])continue;
sum+=dis[i][j]*cnt[i][j];
}
}
return sum;
}
long long answer=INF,ans;
void SA()
{
double t=T0;
for(int i=1;i<=m;i++)b[i]=m-i+1;
ans=INF;
while(t>Tk)
{
long long ex=rand()%m+1,ey=rand()%m+1;
if(ex==ey)continue;
swap(b[ex],b[ey]);
long long eans=calculate();
double de=eans-ans;
if(de<0)ans=eans;
else
{
if(exp(-de/t)*RAND_MAX<rand())swap(b[ex],b[ey]);
}
t*=TD;
}
answer=min(answer,ans);
return;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>n>>m>>st;
srand(2.19);
double beg=clock();
for(int i=0;i<n;i++)a[i+1]=st[i]-'a'+1;
for(int i=2;i<=n;i++)cnt[min(a[i],a[i-1])][max(a[i],a[i-1])]++;
while((double)(clock()-beg)/CLOCKS_PER_SEC<0.9)SA();
cout<<answer;
return 0;
}