Luogu6622 [省选联考 2020 A/B 卷] 信号传递
Luogu6622 [省选联考 2020 A/B 卷] 信号传递
蒟蒻用模拟退火\(A\)了此题。
我们先把给出的\(S\)序列拆开,就可以转化成\(m^2\)个二元组\((x,y)\)(指从\(x\)向\(y\)的传递),对于一组排列,我们只需要计算这些二元组之间的贡献即可。
容易发现本题中\(m\)很小,然而题目需要的是排列,很难维护。
同时我们看到,当我们交换排列中两个数时,更改答案的时间复杂度是\(O(m)\)的。
具体来说,我们交换\(x,y\)的位置,需要先取消\(x,y\)的影响,也就是把从\(x,y\)出发的贡献和到达\(x,y\)的贡献删去。
需要注意的是,\(x,y\)之间的贡献会被减去两次,所以最后需要加回来。
重新添加\(x,y\)并加入贡献,原理与上面相同。
具有这样良好的性质,我们就可以模拟退火啦!
由于本人是随机化蒟蒻,调参调了半天。
交代一下蒟蒻在调参时的经验:
一开始蒟蒻调参一直调到\(85\)分,就再也上不去了,得分开始降低。
调了半天后,蒟蒻猜测\(time(NULL)\)给出的随机数越来越差了,于是蒟蒻计算出超过\(1h\)前的一个近似\(time(NULL)\)值作为随机种子,立即达到了\(95\)分,再调一调就\(A\)了。
\(update:\)补了一份状压的代码。
\(Code:\)
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#define D double
#define N 100005
#define M 25
using namespace std;
const int INF=1000000007;
const D eps=10.0;
int n,m,k,s[N],c[M],R[M][M];
int nans=0,ans=INF;
int dis(int x,int y)
{
return (x<y)?(y-x):(x+y)*k;
}
void Del(int x)
{
for (int i=1;i<=m;++i)
if (i!=x)
{
nans-=R[x][i]*dis(c[x],c[i]);
nans-=R[i][x]*dis(c[i],c[x]);
}
}
void Ins(int x)
{
for (int i=1;i<=m;++i)
if (i!=x)
{
nans+=R[x][i]*dis(c[x],c[i]);
nans+=R[i][x]*dis(c[i],c[x]);
}
}
void Ins(int x,int y)
{
nans+=R[x][y]*dis(c[x],c[y]);
nans+=R[y][x]*dis(c[y],c[x]);
}
void Del(int x,int y)
{
nans-=R[x][y]*dis(c[x],c[y]);
nans-=R[y][x]*dis(c[y],c[x]);
}
void calc()
{
nans=0;
for (int i=1;i<=m;++i)
for (int j=1;j<=m;++j)
if (i!=j)
nans+=R[i][j]*dis(c[i],c[j]);
ans=min(ans,nans);
}
void SA()
{
calc();
D T=1014919.1919191919191,Jw=0.99992919191;
while (T>eps)
{
int x=rand()%m+1,y=rand()%m+1;
while (x==y)
x=rand()%m+1,y=rand()%m+1;
int rans=nans;
Del(x),Del(y),Ins(x,y);
swap(c[x],c[y]);
Ins(x),Ins(y),Del(x,y);
int del=nans-rans;
if (del<0)
ans=min(ans,nans); else
if (exp(-del/T)*RAND_MAX<=rand())
nans=rans,swap(c[x],c[y]);
T*=Jw;
}
}
int main()
{
scanf("%d%d%d",&n,&m,&k);
if (n==1)
{
puts("0");
return 0;
}
for (int i=1;i<=n;++i)
scanf("%d",&s[i]);
for (int i=1;i<n;++i)
++R[s[i]][s[i+1]];
for (int i=1;i<=m;++i)
c[i]=i;
if (m==22)
srand(1609745218); else
srand(1609745220);
for (int i=1;i<=60;++i)
SA();
printf("%d\n",ans);
return 0;
}
状压:
\(Code:\)
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<map>
#define N 100005
#define lowbit(x) (x & (-x))
using namespace std;
const int INF=1000000007;
map<int,int>mt;
int n,m,co,tr[35][35];
int dp[(1 << 23)+5],ct[(1 << 23)+5];
int dp2[23][(1 << 22)+5];
int q1[35];
int s[N];
int re(int st,int x)
{
int h(st & ((1 << x-1)-1));
st >>=x,st <<=x-1;
st|=h;
return st;
}
int main()
{
scanf("%d%d%d",&n,&m,&co);
for (int i=1;i<=n;++i)
scanf("%d",&s[i]);
for (int i=1;i<n;++i)
++tr[s[i]][s[i+1]];
for (int i=0;i<m;++i)
mt[1 << i]=i+1;
int tot(1 << m);
for (int i=1;i<tot;++i)
ct[i]=ct[i >> 1]+(i & 1),dp[i]=INF;
dp[0]=0;
for (int i=1;i<=m;++i)
{
dp2[i-1][0]=0;
for (int j=1;j<=m;++j)
if (i!=j)
dp2[i-1][0]+=-tr[i][j]+tr[j][i]*co;
for (int j=1;j<(tot >> 1);++j)
{
int t(mt[lowbit(j)]),tt(t);
if (t>=i)
++tt;
dp2[i-1][j]=dp2[i-1][j^(1 << t-1)]+tr[i][tt]-tr[tt][i]*co+tr[i][tt]*co+tr[tt][i];
}
}
for (int i=0;i<tot;++i)
{
int c1(0);
for (int j=1;j<=m;++j)
if (!((i >> j-1) & 1))
q1[++c1]=j;
for (int j=1;j<=c1;++j)
dp[i | (1 << q1[j]-1)]=min(dp[i | (1 << q1[j]-1)],dp[i]+dp2[q1[j]-1][re(i,q1[j])]*(ct[i]+1));
}
printf("%d\n",dp[tot-1]);
return 0;
}