MnZn 21pts模拟退火求助
查看原帖
MnZn 21pts模拟退火求助
759274
Stevehim楼主2023/8/25 17:07

rt

#include <bits/stdc++.h>
#define maxn 20010
#define down 0.9999900001
using namespace std;
const double eps = 1e-5;
int sd[maxn] = {0}; //表示距离前缀和
int sw[maxn] = {0}; //表示重量前缀和
int S[maxn] = {0};
int tmp[maxn];
//bool fac[maxn]; //表示每个工厂与哪颗树重合,注意:在山脚下的锯木厂新建一个不存在的树,不然碰到无法判定
//其实不用链表
int best = 0x3f;
int n;

int getsum(int l,int r){
	int dis = sd[r] - sd[l],weight = sw[l];
	return (S[r] - S[l] - dis * weight);
}

int calc(int x,int y) {
	if(x > y) swap(x,y);
	return S[x]  + getsum(x,y) + getsum(y,n);
}
//注意:最后一颗树可能和锯木厂是重合的
void SA() {
	int last = 0x3f;
	double T = 1000;
	while (T > eps) {
		int t1 = rand() % n + 1;
		int t2 = rand() % n + 1;
		while(t1 == t2){ 
			t1 = rand() % n + 1;
			t2 = rand() % n + 1;
		}
		int num = calc(t1,t2);
//		cout << num << endl;
		if(num - last < 0){ //接受最优解
			last = num;
			best = min(best, last);
		}else if(exp(-(num - last) * T) * RAND_MAX > rand()){
			last = num;
		}
		T *= down;
	}
}

int w,d,dl;
int main() {
	srand((unsigned)time(NULL));
	scanf("%d", &n);
	for (int i = 1; i <= n; i++) {
		scanf("%d %d", &w, &d);
		sw[i] = sw[i - 1] + w;
		sd[i] = sd[i - 1] + dl;
		S[i] = S[i - 1] + sw[i - 1] * dl;
		dl = d;
	}
	sw[n + 1] = sw[n] + w;
	sd[n + 1] = sd[n] + d;
	S[n + 1] = S[n] + sw[n] * d;
	n++;
	for(int i = 1; i <= 2; i++) SA();
	cout << best;
	return 0;
}
2023/8/25 17:07
加载中...