題目
題目大意
一個數軸上有n個點,每個點的座標均爲正整數,每個點有一個美麗值。
給定常數p,從原點出發,只能向正方向移動,每次必須停在一個點上,假設經過的距離爲d,那麼這次hike的疲勞值爲。現在要從原點前往最左端的點,求總疲勞值/總美麗值的最小值。
總美麗值爲停留的點的美麗值之和。
題目分析
分數規劃
注意到求兩個和式的比值,並且以一個點爲終點就要對疲勞值、美麗值都產生貢獻。
所以可以考慮01分數規劃
1.二分一個答案,然後令新點權爲 上個點到這裏的距離-二分答案*該點美麗值
2.dp求解判斷是否合法
關於01分數規劃
動態規劃
現在問題就轉化爲了在跳躍的過程中獲得最小點權。
考慮最簡單的動態規劃:表示到i號點的時候的最小點權。
那麼
其中c表示點權,
代碼
下附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]);
}
}