一道模拟退火练习题

题意

为了打击敌人的飞行设施,我军正是准备利用飞行轨道中的一个高科技武器——全手动化定时炸弹来打击敌人。通过用望远镜仔细观察发现,发现敌人的飞机均匀地停放在一个圆形的飞机坪跑道上,为了方便计算距离,我军把飞机跑道均分成了L等份,并从某个等分点开始按顺时针方向从0开始依次标号到L-1,敌人的飞机也正好落在了这些等分点上。
现在有N架飞机停在了跑道上,同时也有N个定时炸弹被我军地下组织人员事先放在了飞行轨道上,我军准备派遣了一个由N人组成的敢死队小分队去对这N架飞机都被安放上定时炸弹。
安防炸弹有一定的风险值,假定每移动一个单位距离,风险值就增加1,那么剩下的问题就是,该如何为这些炸弹指定安装目标,才能使得总风险值达到最小。

数据范围

L < = 1 e 9     N < = 1 e 5 L<=1e9~~~N<=1e5

解法

首先考虑如果把炸弹当做一排点,飞机当做一排点,并且将飞机炸弹分别拍好序,跑二分图最大权匹配,那么是一定不会产生交叉的.所以如果确定了第一个炸弹连向的飞机,那么后面的炸弹就都确定了.所以有一个 O ( n 2 ) O(n^2) 的做法,然后考虑答案和第一个炸弹连向的飞机的函数关系,发现可以模拟退火.

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=6e5+5;
const double delta=0.98;
const int inf=0x3f3f3f3f;
inline int read(){
	char c=getchar();int t=0,f=1;
	while((!isdigit(c))&&(c!=EOF)){if(c=='-')f=-1;c=getchar();}
	while(isdigit(c)&&(c!=EOF)){t=(t<<3)+(t<<1)+(c^48);c=getchar();}
	return t*f;
}
int l,n,p[maxn],q[maxn];
int pos[maxn],m,vis[maxn];
inline int dis(int x,int y){
	if(x>y)swap(x,y);
	return min(y-x,l-y+x);
}
inline int calc(int x){
	int tmp=0;
	for(int i=1;i+x-1<=n;i++){
		tmp=tmp+dis(q[i+x-1],p[i]);
	}
	for(int i=n-x+2;i<=n;i++){
		tmp=tmp+dis(q[i+x-n-1],p[i]);
	}
	return tmp;
}
void SA(){
	int ans=1ll<<60,res;
	int nowpos=1;
	int st=clock(),dfn=1;
	double T=1e6;
	while(clock()-st<=0.5*CLOCKS_PER_SEC){
		int tmp=T*rand()/RAND_MAX;
		tmp=nowpos-1+(rand()&1?1:-1)*tmp;
		tmp=(tmp%n+n)%n+1;
		if(vis[tmp])continue;
		vis[tmp]=1;
		res=calc(tmp);
		//printf("%lld %lld\n",tmp,res);
		if(res<ans){
			ans=res;
			nowpos=tmp;
		}
		else if(exp((ans-res)/T)>(double)rand()/RAND_MAX){
			nowpos=tmp;
		}
		T*=delta;
	}
	printf("%lld\n",ans);
}
signed main(){
	//freopen("2.in","r",stdin);
	//freopen("2.out","w",stdout);
	l=read(),n=read();
	for(int i=1;i<=n;i++){
		p[i]=read();
	}
	for(int i=1;i<=n;i++){
		q[i]=read();
	}
	sort(p+1,p+1+n);
	sort(q+1,q+1+n);
	srand(20200113);
	SA();
	return 0;
}

发布了95 篇原创文章 · 获赞 9 · 访问量 3206

猜你喜欢

转载自blog.csdn.net/wmhtxdy/article/details/103957013