SLF改造计划(三分枚举)(AOJ 853)

AOJ 853 点此打开链接


SLF 改造计划
Time Limit: 10000 ms   Case Time Limit: 1000 ms   Memory Limit: 256 MB
Total Submission: 29   Submission Accepted: 11
Judge By Case
Description
俗话说得好,精卫填海,LF 平山。作为处女座的 SLF 强迫症有很多,他学成后买下一个荒无人烟的山丘地带,但是山的高度很是令他烦恼,于是他决定要用最小的代价让最高的山峰与最低的山峰的高度差不超过 17,SLF 经过调查,已知第 i 座山峰高度为 a[i],由于填山或是平山都需要代价,SLF 询问了专业人员,将高度为 a[i]的山峰改造成高度为 x 的山峰的代价为(a[i] – x)^ 2,经过苦难的他深知赚钱的不容易,所以他希望代价最小。

Input
第一行一个数 n。
接下来 n 行每行一个整数 a[i](0 ≤ a[i] ≤ 100),表示山峰高度。

Output
一行一个整数,最小的代价

Sample Input
5
20
4
1
24
21

Sample Output
18

Hint
对于 100%的数据,1 ≤ n ≤ 1000


思路1

我一开始的想法是先sort,然后判断两边的高度差是否大于17,如果大于就对两边的山峰折中改变,改变后插入两边适当位置(插入后满足排序)再继续执行这样的操作,直到满足条件。结论证明这种思路不是最优的。因为每一次的改变可能不是最优的

于是转到枚举的思路:先找到最高的山峰(最低也行),枚举一个高度(0~最高)为参考标准,对于每一个标准进行枚举。确定一个标准后,大于标准的山峰直接降到标准;小于标准并且差值大于17的,升到标准-17的高度,这样计算总花费并维护出最小值即为所求。


代码示例1

#include<bits/stdc++.h>
using namespace std;

const int inf=0x3fffffff;

int arr[110];

int sq(int x){
	return x*x;
}

int main()
{
	std::ios::sync_with_stdio(false);
	int min=inf;
	int max,n,temp;
	max=-inf;
	cin>>n;
	for(int i=0;i<n;++i){
		cin>>arr[i];
		if(arr[i]>max) max=arr[i];
	}
	for(int i=0;i<=max;++i){
		temp=0;
		for(int j=0;j<n;++j){
			if(arr[j]>i) temp+=sq(arr[j]-i);
			else if(i-arr[j]>17){
				temp+=sq(i-arr[j]-17);
			}
		}
		if(temp<min) min=temp;
	}
	cout<<min<<endl;
	return 0;
}



思路2

由于这题的数据量较小,如果数据量增大,暴力枚举可能会超时,下面是三分枚举的方法

这里代价的计算形式为(a[i] – x)^ 2,二次函数加上常数17的移动,可知代价关于枚举的标准是单峰函数

所以可以尝试三分枚举


三分模板

double calculate(type a)
{
	//根据题目进行计算 
}

void solve(void)
{
	double left,right;
	double mid,midmid;
	double mid_value,midmid_value;
	left=MIN;
	right=MAX;
	while(right-left>eps)
	{
		mid=(left+right)/2;
		midmid=(mid+right)/2;
		mid_value=calculate(mid);
		midmid_value=calculate(midmid);
		if(mid_value>=midmid_value) left=mid;//以极小值为例 
		else right=midmid;
	}
	//处理结果left(或right) 
}


代码示例2

#include<bits/stdc++.h>
using namespace std;
const int maxn=1000;
//const int inf=0x3fffffff;
int arr[maxn+50];
int n;

int sq(int x)
{
	return x*x;
}

int filter(int j)
{
	int s=0;
	for(int i=1;i<=n;++i){
		if(arr[i]>j) s+=sq(arr[i]-j);
		else if(j-arr[i]>17) s+=sq(j-arr[i]-17);
	}
	return s;
}

int main()
{
	while(scanf("%d",&n)!=EOF)
	{
		for(int i=1;i<=n;++i){
			scanf("%d",&arr[i]);
		}
		int maxx=arr[1];
		for(int i=2;i<=n;++i){
			if(arr[i]>maxx) maxx=arr[i];
		}
		int l=0,r=maxx;
		int mid,midmid;
		while(l+2<r)//由于这里l,r是int类型,所以标准定到2
		{
			mid=(l+r)/2;
			midmid=(mid+r)/2;
			if(filter(mid)>filter(midmid)) l=mid;
			else r=midmid;
		}
		int ans=filter(l);
		for(int i=l;i<=r;++i){
			if(filter(i)<ans) ans=filter(i);
		}
		printf("%d\n",ans);
	}
	return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值