CF489E:hiking-01分數規劃

題目

題目鏈接

題目大意

一個數軸上有n個點,每個點的座標均爲正整數,每個點有一個美麗值。

給定常數p,從原點出發,只能向正方向移動,每次必須停在一個點上,假設經過的距離爲d,那麼這次hike的疲勞值爲abs(dp)\sqrt{abs(d-p)}。現在要從原點前往最左端的點,求總疲勞值/總美麗值的最小值。

總美麗值爲停留的點的美麗值之和。

題目分析

分數規劃

注意到求兩個和式的比值,並且以一個點爲終點就要對疲勞值、美麗值都產生貢獻。

所以可以考慮01分數規劃

1.二分一個答案,然後令新點權爲 上個點到這裏的距離-二分答案*該點美麗值

2.dp求解判斷是否合法

關於01分數規劃

動態規劃

現在問題就轉化爲了在跳躍的過程中獲得最小點權。

考慮最簡單的動態規劃:dp[i]dp[i]表示到i號點的時候的最小點權。

那麼dp[i]=min(dp[k]+c[i])dp[i]=min(dp[k]+c[i])

其中c表示點權,k(1,i1)k∈(1,i-1)

代碼

下附AC代碼:

#include<bits/stdc++.h>
using namespace std;
int read(){
	char s;
	int x=0,f=1;
	s=getchar();
	while(s<'0'||s>'9'){
		if(s=='-')f=-1;
		s=getchar();
	}
	while(s>='0'&&s<='9'){
		x*=10;
		x+=s-'0';
		s=getchar();
	}
	return x*f;
}
const int N=1010;
int n,len;
int x[N],c[N];
double dp[N];
int pre[N];//記錄答案路徑 
bool check(double limit){
	for(int i=1;i<=n;i++){
		dp[i]=1e9;
		for(int j=0;j<i;j++){
			double tmp=dp[j]+sqrt(abs(len-(x[i]-x[j])))-limit*c[i];
			if(tmp<dp[i]){
				pre[i]=j;
				dp[i]=tmp;
			}
		}
	}
	return dp[n]>0;
}
int ans[N];
int main(){
	n=read(),len=read();
	for(int i=1;i<=n;i++){
		x[i]=read(),c[i]=read();
	}
	double l=0,r=1e10;
	while(r-l>1e-8){
		double mid=(l+r)/2;
		if(check(mid))l=mid;
		else r=mid;
	}
	check(l);
	int cnt=1;
	int id=n;
	while(id>0){
		ans[cnt++]=id;
		id=pre[id];
	}
	for(int i=cnt-1;i>0;i--){
		printf("%d ",ans[i]);
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章