ABC 315F Shortcuts

Linear_L發表於2024-06-09

題意
有N個點,你從第一個點出發,要按順序經過所有的點,最終抵達第N個點。在這個過程中你可以跳過一些點,但如果你跳過了C個點,那麼你必須要接受pow(2,C-1)的懲罰。設s為你走過的距離加上你的懲罰,請求出最小化s。

題解
顯然考慮dp,設計dp[i][j]為到達第i個點,中途跳過了j個點需要的路程。考慮到跳過懲罰為指數函式,那麼跳躍的點一定不會太多,那麼這樣我們可以列舉j<=30即可。在狀態轉移的過程中我們考慮。當到達dp[i][j]時,我們要列舉的東西是:

  1. 第i個點由哪一個點j跳躍過來(max(1,i-30)<=j<i)
  2. 列舉已經跳躍的步數(注意,是“已經跳躍的步數”,因為從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;
 }