P11188 题解
Aventurine_stone · · 题解
1. 题目分析
这是一篇模拟退火题解。
我第一眼看到这道题就知道这道题是动态规划或者贪心。
但是我太菜了推不出式子,于是只能另辟蹊径。
2. 题目做法
仔细观察数据范围,
我们首先想到暴力搜索,但搜索的方案数实在是太过庞大,时间复杂度是不允许的。我们发现每次方案变动对答案的影响都不是很大,故不难想此题的所有方案的值是一个连续的多峰函数,于是我们可以考虑用模拟退火来解决此题。
所以我们可以随机这
若
我们用一个
有一些优化我放代码的注释里了,此题时间限制较宽裕,不加优化应该也能过。
3. 代码
#include<bits/stdc++.h>
#define r() rand()
#define R(l,r) ((double)rand()/RAND_MAX*(r-l))
#define ll long long
using namespace std;
const int N=100010;
inline int read()
{
int x=0;
char c=getchar();
while(c<'0'||c>'9')
c=getchar();
while(c>='0'&&c<='9')
x=(x<<1)+(x<<3)+c-'0',c=getchar();
return x;
}
int C,t;
char c[N];
int n,v[10],a[N];
double T,dt;
int to[7],have[N],num;
ll sum,ans,tt,now,fu;
inline ll change(int x,int y)
{
have[to[x]]==1?sum+=v[a[to[x]]]:1;
!have[y]?sum-=v[a[y]]:1;
to[x]=y;
num=a[to[1]];
for(int i=2;i<=5;i++)
to[i]!=to[i-1]?num=(num<<1)+(num<<3)+a[to[i]]:1;
tt=sum+num;
tt<ans?ans=tt:1;
return tt;
}
void SA()
{
T=1e5;
sum=0;
for(int i=1;i<=n;i++)
sum+=v[a[i]];
ans=sum;
for(int i=1;i<=5;i++)
to[i]=r()%n+1,have[to[i]]++;
sort(to+1,to+6);
num=a[to[1]],sum-=v[a[to[1]]];
for(int i=2;i<=5;i++)
to[i]!=to[i-1]?num=(num<<1)+(num<<3)+a[to[i]],sum-=v[a[to[i]]]:1;
now=sum+num;
ans=min(ans,now);
while(T>1e-7)
{
//最好让 to 数组保持升序,这样每次改变都不用让 to 数组重新排序了
int x=rand()%5+1,y=r()%(to[x+1]-to[x-1]+1)+to[x-1];
if(to[x]==y)//此时不用进行更改
continue;
int g=to[x],numg=num;
ll sumg=sum;
fu=change(x,y);
dt=now-fu;
if(exp(dt/T)>R(0,1))
now=fu,have[g]--,have[y]++;
else
to[x]=g,sum=sumg,num=numg;
T*=0.9999;
}
for(int i=1;i<=5;i++)
have[to[i]]--;
}
int main()
{
C=read(),t=read();
to[0]=1;
while(t--)
{
scanf("%s",c+1);
n=strlen(c+1);
to[6]=n;
for(int i=1;i<=n;i++)
a[i]=c[i]-'0';
for(int i=1;i<=9;i++)
v[i]=read();
if(n==1)//特判,否则我的程序会进死循环
{
printf("%d\n",min(a[1],v[a[1]]));
continue;
}
SA();
printf("%lld\n",ans);
}
return 0;
}