1. 程式人生 > 其它 >洛谷 P3878 [TJOI2010]分金幣 題解報告

洛谷 P3878 [TJOI2010]分金幣 題解報告

題目連結
此題與 P2210 最大區別是如果產生的值不如原先值更優,需要把兩個金幣的位置再換回去、

點選檢視程式碼
#include<bits/stdc++.h>
#define d 0.996//降溫幅度 
#define lim 1e-10//停止溫度
#define INF 1e9+7
using namespace std;
int n,t;
int a[35];
int ans=INF;
int del,now,nowx,nowy;
int calc()
{
	int sum1=0,sum2=0;
	for(int i=1;i<=n/2;i++) sum1+=a[i];//前一半 
	for(int i=n/2+1;i<=n;i++) sum2+=a[i];//後一半 	 
	return abs(sum1-sum2);//價值差 
}
void sa()
{
	double T=2021;//初始溫度 
	while(T>lim)
	{
		nowx=rand()%n+1;//產生1~n內的兩個隨機種子 
		nowy=rand()%n+1;
		swap(a[nowx],a[nowy]);//交換兩個金幣的值 
		now=calc();
		del=now-ans;
		if(del<0) ans=now;//now比ans小,說明此時now更優 
		else if(exp(-del/T)<(double)rand()/RAND_MAX){//否則一定概率接受此解 
		}
		else swap(a[nowx],a[nowy]);//如果產生的now不如ans更優,需要把兩個值再換回去,以此來穩定最優解 
		T*=d;
	}
	}
}
void work()
{
	for(int i=1;i<=100;i++) sa();//多跑幾次 
}
int read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9')
	{
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*f;
}
int main()
{
	t=read();
	while(t--)
	{
		n=read();
		for(int i=1;i<=n;i++) a[i]=read();//輸入 
		ans=INF;//開始答案要賦極大值 
		work();
		printf("%d\n",ans);
	}
	return 0;
}