Official

F - Shortcuts Editorial by physics0523


例えば、 100100 個のチェックポイントを無視したとしましょう。

この時 29910302^{99} \approx 10^{30} のペナルティが発生しますが、愚直に全てのチェックポイントを回ったとしても距離の総和は N×105N \times 10^5 以内で収まるので、この数の無視をするのは明らかに無駄であることが分かります。

この時、以下の DP が成立します:
dp[最後に通ったチェックポイント][無視したチェックポイントの数] = {距離の総和の最小値}
二つ目の添字 は 100100 未満しか考慮しなくて良いことに注意してください。

dp[1][0]=0dp[1][0]=0 、その他のテーブルの値は \infty に初期化され、答えは 「 dp[N][i]dp[N][i]ii 個のチェックポイントを無視した際のペナルティを足したもの」の最小値となります。

このことから、考慮する無視の回数の上限を CC としたとき、時間計算量 O(NC2)O(NC^2) でこの問題に正解できます。 ( C=100C=100 とするとギリギリになる可能性がありますが、もう少しよい見積をすると実行時間に余裕を持って AC できます。 )

実装例 (C++):

Copy
  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. using pi=pair<int,int>;
  4. long double dist(pi a,pi b){
  5. int dx=a.first-b.first;
  6. int dy=a.second-b.second;
  7. int ss=dx*dx+dy*dy;
  8. return sqrt((long double)ss);
  9. }
  10. long double lg=8.0e18;
  11. int main(){
  12. int n;
  13. cin >> n;
  14. vector<pi> vp(n);
  15. for(int i=0;i<n;i++){
  16. cin >> vp[i].first >> vp[i].second;
  17. }
  18. vector<vector<long double>> dp(n,vector<long double>(60,lg));
  19. dp[0][0]=0.0;
  20. for(int i=1;i<n;i++){
  21. for(int j=i-1;j>=0;j--){
  22. int skip=i-j-1;
  23. for(int k=0;(k+skip)<60;k++){
  24. dp[i][k+skip]=min(dp[i][k+skip],dp[j][k]+dist(vp[j],vp[i]));
  25. }
  26. }
  27. }
  28. long double res=lg;
  29. for(int i=0;i<60;i++){
  30. long double pen=0.0;
  31. if(i>0){
  32. pen=pow(2.0,(long double)(i-1));
  33. }
  34. res=min(res,dp[n-1][i]+pen);
  35. }
  36. printf("%.6Lf\n",res);
  37. return 0;
  38. }
#include<bits/stdc++.h>
 
using namespace std;
using pi=pair<int,int>;
 
long double dist(pi a,pi b){
  int dx=a.first-b.first;
  int dy=a.second-b.second;
  int ss=dx*dx+dy*dy;
  return sqrt((long double)ss);
}
 
long double lg=8.0e18;
 
int main(){
  int n;
  cin >> n;
  vector<pi> vp(n);
  for(int i=0;i<n;i++){
    cin >> vp[i].first >> vp[i].second;
  }
 
  vector<vector<long double>> dp(n,vector<long double>(60,lg));
  dp[0][0]=0.0;
  for(int i=1;i<n;i++){
    for(int j=i-1;j>=0;j--){
      int skip=i-j-1;
      for(int k=0;(k+skip)<60;k++){
        dp[i][k+skip]=min(dp[i][k+skip],dp[j][k]+dist(vp[j],vp[i]));
      }
    }
  }
 
  long double res=lg;
  for(int i=0;i<60;i++){
    long double pen=0.0;
    if(i>0){
      pen=pow(2.0,(long double)(i-1));
    }
    res=min(res,dp[n-1][i]+pen);
  }
 
  printf("%.6Lf\n",res);
  return 0;
}

posted:
last update:



2025-04-05 (Sat)
15:36:59 +00:00