题目:
题意:
给出
n
n
n个木材,各自的重量和下一个木材之间的距离
在最后一个位置设有木材厂,同时我们也可以自己设置两个木材厂在任意位置,所有木材只会向编号大的方向移动,直到第一个木材厂为止,花费为移动距离
∗
*
∗重量
问最少花费和是多少,在两个地方设木材厂能做到
分析:
模拟退火真是
x
s
w
l
xswl
xswl
首先分析下花费的求法,假设
a
,
b
a,b
a,b表示两个木材厂的位置,就有
=
∑
i
=
1
a
w
i
∗
∣
a
−
i
∣
+
∑
i
=
a
+
1
b
w
i
∗
∣
b
−
i
∣
+
∑
i
=
b
+
1
n
w
i
∗
∣
n
−
i
∣
=\sum_{i=1}^aw_i*|a-i|+\sum_{i=a+1}^bw_i*|b-i|+\sum_{i=b+1}^nw_i*|n-i|
=∑i=1awi∗∣a−i∣+∑i=a+1bwi∗∣b−i∣+∑i=b+1nwi∗∣n−i∣
再设
d
i
d_i
di表示
1
∼
i
−
1
1\sim i-1
1∼i−1的距离,用下乘法分配律可以转换成这个亚子:
d
a
∗
∑
i
=
1
a
w
i
+
d
b
∗
∑
i
=
a
+
1
b
w
i
+
d
n
+
1
∗
∑
i
=
b
+
1
n
w
i
−
∑
i
=
1
n
w
i
∗
d
i
d_a*\sum_{i=1}^aw_i+d_b*\sum_{i=a+1}^bw_i+d_{n+1}*\sum_{i=b+1}^nw_i-\sum_{i=1}^nw_i*d_i
da∗i=1∑awi+db∗i=a+1∑bwi+dn+1∗i=b+1∑nwi−i=1∑nwi∗di
所有考虑把
w
w
w和
d
d
d用前缀和搞出来,而
a
,
b
a,b
a,b用模拟退火一位位移算下就
o
j
b
k
ojbk
ojbk了
比较特殊的是
t
l
a
s
t
t_{last}
tlast是无限趋近与
0.5
0.5
0.5的就够了,因为当
t
t
t在
0.5
0.5
0.5以下时对新的
a
,
b
a,b
a,b是不会有影响的,可以,但没必要
代码:
#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#include<cmath>
#include<cstdlib>
#include<ctime>
#define LL long long
#define down 0.99
using namespace std;
inline LL read() {
LL d=0,f=1;char s=getchar();
while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
while(s>='0'&&s<='9'){d=d*10+s-'0';s=getchar();}
return d*f;
}
LL n=read();
LL w[20005],d[20005],s;
struct qwq{
LL a,b;
LL get()
{
if(a>b) swap(a,b);
return w[a]*d[a]+(w[b]-w[a])*d[b]+(w[n]-w[b])*d[n+1]-s;
}
}best,now,odd;
void sa()
{
double t=n;
odd.a=rand()%n+1; odd.b=rand()%n+1;
while(t>0.5)
{
int aa=round((2.0*rand()/RAND_MAX-1)*t);
int bb=round((2.0*rand()/RAND_MAX-1)*t);
now.a=(odd.a+aa+n)%n+1;
now.b=(odd.b+bb+n)%n+1;
LL dif=now.get()-odd.get();
if(dif<0) odd=now;
else if(exp(-dif/t)*RAND_MAX>rand()) odd=now;
if(best.get()>odd.get()) best=odd;
t*=down;
}
return;
}
int main()
{
srand(19260817);
for(LL i=1;i<=n;i++)
{
LL a=read(),b=read();
w[i]=w[i-1]+a;d[i+1]=d[i]+b;
s+=a*d[i];
}
best.a=1;best.b=2;
for(int i=1;i<=233;i++) sa();
cout<<best.get();
return 0;
}