Hatena::Grouptopcoder

cafelier@SRM

cafelier のSRM参加記録です。コンテスト中に考えてたことを執拗に全部書き残すとどうなるだろうかという試み本番中にこういうコードが書きたかったなあ、という後で書いた反省コードを書き残す試み

スパムが来たのでしばらくコメント欄をはてなユーザ限定にしています、すみません、

 | 

2010-12-29

SRM492

| 21:22 | はてなブックマーク -  SRM492 - cafelier@SRM

SRM492 の成績・ソース (要ログイン) : WA/WA/- : 過去最悪

  • 燃え尽きたので簡単に…

250開く

  • 『x軸上の整数座標のところにN本、整数の高さの木が生えています。木の高さを適当に低くできるので、木のてっぺんが一直線上に並ぶようにしてください。高さを変える木の数を最小に。』
    • N≦50
    • 座標値の範囲は≦1000
    • 高さは整数以外に変えてもいいので注意

  • DPで左から順に高さを変えていって…
    • この木をこの高さにする場合の最小コスト、みたいなの
  • ああだめだ。高さ整数じゃなくてもいいのか。任意の高さまで考えると表が作れない
  • でも無限の可能性は考える必要ないよな。
  • x軸の間隔分の1くらいの有理数まで考えれば
  • いやーでもそれは最低でも1000×1000の表が必要そうだし計算量間に合わない

  • もっと巧い考え方があるんだよな…
  • 二本の高さを決めれば
  • 一直線上に乗せるので、残りの高さは決まる。
  • これだ。
  • 二本選んで
  • 高さを決めて
  • 線を引いて他の木が全部それより上ならOK。

  • パターンの数は
  • 二本選ぶのが 50x50 で
  • 高さを決めるのが1000x1000で…って無理だし、違う、整数とは限らないんだった。

  • うーむ
  • 高さ変わらない二本が必ずあるのでは
  • ありそうな気がする。
  • まず、全部の木の高さを減らすくらいなら平行移動して1本変えなくできる
  • 1本だけ残す場合も、できる直線を回したらもう一本別のに引っかかるまで回せて…
  • そうでもないか地面にひっかかる。
  • まあだいたい高さ変わらない1本か2本があるので、それだ。

  • ※この辺りで木の高さではなく数の最大値を1000と勘違いし始める。
  • これは二本固定して他の木全部の高さをチェックするので…
  • 10003?無理だー

  • いやでも、1本固定して、可能な傾きの範囲を1パスで求めて
  • その範囲にある傾きをmap<傾き,int>などに入れて重複が一番多いところを選ぶとかすれば
  • 10002
  • これだー
  • 傾きの区間とかを有理数で管理するのめんどいなあ…
  • もう30分以上時間使ってるしこのまま他の問題解けないと順位ヤバいし…
  • ええい手抜き
  • mapのキーだけ分子分母ペアにして比較はdoubleにしてしまえ
  • 通分した整数割り算しかやらないので誤差は生じないはず…

  • 書いた。submit

550開く

  • 『無向グラフの頂点を、指定された順番に訪れる。(途中で他の頂点を通ってもいい)。過去に向かってタイムトラベルできるので、過去に訪れた町には時間を戻してゼロコストで行ける。コスト最小化せよ』
    • 50頂点。頂点リストの長さも50くらい

  • 面白い問題設定だな。
  • タイムトラベルじゃなくてルーラが使えるなら最小全域木…ではないか、シュタイナー木か
  • だけど、過去に戻ってしまうと未来に行った頂点には飛べなくなる。ふむむ。

  • 考え方としてはツリーだよね。
  • スタートの0番頂点をルートにする。
  • 普通に道を辿って歩くときは子ノードにする。
  • タイムトラベルは、祖先にジャンプ、という操作

  • こう考えると
    • (木の)各ノードにグラフの頂点番号が乗っていて
    • (木の)辺の重みはグラフでの対応する距離で
    • 根は0番で
  • あるようなツリーで、DFSでたどったら指定の頂点訪問リストをsubsequenceとして含んでるような物
  • のうち辺の重み和が最小のもの

  • を求めればいい
  • これ、前回のtrieを最小化する問題に似てる気がするな。
  • 子ノードの個数が任意だからやっかいっぽく見えるけど、
  • 単に担当を二分割する再帰でいける

  • rec(cur, s, e)
  • で、頂点curをルートとする、さっきの条件を満たしていて、
  • 頂点リストsからeまでを訪れられる最小コスト

  • を求めるようにする。よしこれだ
  • うわーしかし時間が間に合わない
  • あ、これサイクルで無限ループする可能性が
  • 何回も回ったらいいことないからループはすぐに∞返せばいいか。
  • うひー残り1分
  • サンプルが…1個通らない!?あれー
  • いいや思い出submit…
  • (めでたくchallengeされました)


SRM492 550

| 13:14 | はてなブックマーク -  SRM492 550 - cafelier@SRM

  • 505/6 のオーダだけどまあ無理矢理
static const LL INF = 0x1fffffffffffffffLL;

template<typename T>
struct DP3
{
   int N1, N2, N3;
   vector<T> data;
   DP3(int N1, int N2, int N3, const T& t = T())
      : N1(N1), N2(N2), N3(N3), data(N1*N2*N3, t) { assert(data.size()*sizeof(T)<(1<<26)); }
   T& operator()(int i1, int i2, int i3)
      { return data[ ((i1*N2)+i2)*N3+i3 ]; }
   void swap(DP3& rhs)
      { data.swap(rhs.data); }
};

class TimeTravellingTour { public:
   long long determineCost(int N, vector <int> cities, vector <string> roads) 
   {
      vector< vector<LL> > G(N, vector<LL>(N,INF));
      {
         string str = accumulate(roads.begin(), roads.end(), string(""));
         for(int i=0; i<str.size(); ++i)
            if( str[i] == ',' )
               str[i] = ' ';
         stringstream sin(str);
         for(int a,b,cost; sin>>a>>b>>cost; )
            G[a][b] = G[b][a] = cost;
      }
      for(int i=0; i<N; ++i)
         G[i][i] = 0;
      for(int k=0; k<N; ++k)
         for(int i=0; i<N; ++i)
            for(int j=0; j<N; ++j)
               G[i][j] = min(G[i][j], G[i][k]+G[k][j]);

      DP3<LL> memo(N, cities.size()+1, cities.size()+1, -1);
      LL a = rec( 0, 0, cities.size(), cities, G, memo );
      return a>=INF ? -1 : a;
   }

   LL rec(int cur, int s, int e, const vector<int>& C, const vector< vector<LL> >& G, DP3<LL>& memo )
   {
      if( s+1 == e )
         return G[cur][C[s]];
      if( memo(cur,s,e) >= 0 )
         return memo(cur,s,e);

      LL best = INF;
      for(int v=0; v<G[cur].size(); ++v)
         if( G[cur][v] < INF )
            for(int m=s+1; m<e; ++m)
               best = min(best, G[cur][v]+rec(v,s,m,C,G,memo)+rec(v,m,e,C,G,memo));
      return memo(cur,s,e) = best;
   }
};

SRM492 250

| 13:49 | はてなブックマーク -  SRM492 250 - cafelier@SRM

  • 配列サイズ1000と思い込んでいてO(N2)で解くために凝ったことをして間違えたあげくチャレンジフェーズでも思い込みを継続して2撃墜ミス…
typedef complex<double> CMP;
double outer_prod(const CMP& a, const CMP& b) { return imag(conj(a)*b); }
double inner_prod(const CMP& a, const CMP& b) { return real(conj(a)*b); }
int ccw(const CMP& a, CMP b, CMP c) {
   b -= a; c -= a;
   if( outer_prod(b,c) > 0 ) return +1; // counter clockwise
   if( outer_prod(b,c) < 0 ) return -1; // clockwise
   if( inner_prod(b,c) < 0 ) return +2; // c--[a--b] on line
   if( norm(b) < norm(c) )   return -2; // [a--b]--c on line
   return 0; // [a--c--b] on line
}

class TimeTravellingGardener { public:
   int determineUsage(vector <int> distance, vector <int> height) 
   {
      const int N = height.size();

      distance.insert(distance.begin(), 0);
      partial_sum(distance.begin(), distance.end(), distance.begin());

      vector<CMP> p;
      for(int i=0; i<N; ++i)
         p.push_back( CMP(distance[i], height[i]) );

      int best = N-1;
      for(int i=0; i<N; ++i)
         for(int j=i+1; j<N; ++j)
         {
            best = min(best, score(p[i], p[j], p));
            best = min(best, score(p[i].real(), p[j], p));
            best = min(best, score(p[i], p[j].real(), p));
         }
      return best;
   }

   int score(const CMP& a, const CMP& b, const vector<CMP>& p)
   {
      int s = 0;
      for(int i=0; i<p.size(); ++i)
      {
         if( ccw(a, b, p[i]) == -1 ) return INT_MAX;
         if( ccw(a, b, p[i].real()) == +1 ) return INT_MAX;
         if( ccw(a, b, p[i]) == +1 ) ++s;
      }
      return s;
   }
};
  • 配列サイズは50です。2カ所固定して残りが全部上にいるか確かめるだけ
トラックバック - https://topcoder-g-hatena-ne-jp.jag-icpc.org/cafelier/20101229
 | 

presented by cafelier/k.inaba under CC0