題意
有N個點,你從第一個點出發,要按順序經過所有的點,最終抵達第N個點。在這個過程中你可以跳過一些點,但如果你跳過了C個點,那麼你必須要接受pow(2,C-1)的懲罰。設s為你走過的距離加上你的懲罰,請求出最小化s。
題解
顯然考慮dp,設計dp[i][j]為到達第i個點,中途跳過了j個點需要的路程。考慮到跳過懲罰為指數函式,那麼跳躍的點一定不會太多,那麼這樣我們可以列舉j<=30即可。在狀態轉移的過程中我們考慮。當到達dp[i][j]時,我們要列舉的東西是:
- 第i個點由哪一個點j跳躍過來(max(1,i-30)<=j<i)
- 列舉已經跳躍的步數(注意,是“已經跳躍的步數”,因為從j跳躍到i是必然會跳一次的,所以我們要列舉的是已經跳躍的點),剩下的就是注意細節就行了,具體實現看程式碼
程式碼
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=1e4+10;
double dp[maxn][100];
struct node
{
int x,y;
}a[maxn];
double dis(node x,node y)
{
return sqrt(1.0*(x.x-y.x)*(x.x-y.x)+1.0*(x.y-y.y)*(x.y-y.y));
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);
int n;
cin>>n;
const int mo=1;
for(int i=1;i<=n;i++) cin>>a[i].x>>a[i].y;
for(int i=1;i<=n;i++) for(int j=0;j<=30;j++) dp[i][j]=1e18;
dp[1][0]=0;
for(int i=2;i<=n;i++)//更新dp[i][j],表示到達第i個點跳躍j個點需要的路程
{
for(int j=max(mo,i-30);j<=i-1;j++)//列舉跳躍點
{
for(int k=0;k<=min(mo*30,i-1);k++)//列舉跳躍的步數
{
if(k-(i-j)+1<0) continue;
dp[i][k]=min(dp[i][k],dp[j][k-i+j+1]+dis(a[i],a[j]));
}
}
}
double ans=dp[n][0];
int p=1;
for(int i=1;i<=30;i++)
{
ans=min(ans,dp[n][i]+pow(2,i-1));
p=p*2;
}
printf("%.7f",ans);
return 0;
}