#include // clang-format off using namespace std;constexpr int INF=1001001001;constexpr long long INFll=1001001001001001001;namespace viewer{using s=string;templates f(T i){s S=i==INF||i==INFll?"inf":to_string(i);return s(max(0,3-int(S.size())),' ')+S;} templateauto v(T&x,s&&e)->decltype(cerr<void v(const pair&p,s&&e="\n"){cerr<<"(";v(p.first,", ");v(p.second,")"+e);}templatevoid v(const tuple&t,s&&e="\n"){cerr<<"(";v(get<0>(t),", ");v(get<1>(t),")"+e);}templatevoid v(const tuple&t,s&&e="\n"){cerr<<"(";v(get<0>(t),", ");v(get<1>(t),", ");v(get<2>(t),")"+e);}templatevoid v(const tuple&t,s&&e="\n"){cerr<<"(";v(get<0>(t),", ");v(get<1>(t),", ");v(get<2>(t),", ");v(get<3>(t),")"+e);} templatevoid v(const vector&vx,s);templateauto ve(int,const vector&vx)->decltype(cerr<auto ve(bool,const vector&vx){cerr<<"{\n";for(const T&x:vx)cerr<<" ",v(x,",");cerr<<"}\n";}templatevoid v(const vector&vx,s){ve(0,vx);} templatevoid v(const deque&q,s&&e){v(vector(q.begin(),q.end()),e);}templatevoid v(const set&S,s&&e){v(vector(S.begin(),S.end()),e);}templatevoid v(const multiset&S,s&&e){v(vector(S.begin(),S.end()),e);}templatevoid v(const unordered_set&S,s&&e){v(vector(S.begin(),S.end()),e);} templatevoid v(const priority_queue&p,s&&e){priority_queueq=p;vectorz;while(!q.empty()){z.push_back(q.top());q.pop();}v(z,e);}templatevoid v(const map&m,s&&e){cerr<<"{"<<(m.empty()?"":"\n");for(const auto&kv:m){cerr<<" [";v(kv.first,"");cerr<<"] : ";v(kv.second,"");cerr<<"\n";}cerr<<"}"+e;} templatevoid _view(int n,s&S,T&var){cerr<<"\033[1;32m"<void grid(T _){}void grid(const vector>&vvb){cerr<<"\n";for(const vector&vb:vvb){for(const bool&b:vb)cerr<<(b?".":"#");cerr<<"\n";}} void _debug(int,s){}templatevoid _debug(int n,s S,const H&h,const T&... t){int i=0,cnt=0;for(;iap;mint re=a;for(long long r=1;r(_end-_start).count();} inline int ms()const{const chrono::system_clock::time_point now=chrono::system_clock::now();return static_cast(chrono::duration_cast(now-_start).count()/1000);} inline int ns()const{const chrono::system_clock::time_point now=chrono::system_clock::now();return static_cast(chrono::duration_cast(now-_start).count());} string report(){return to_string(sum/1000000)+"[ms]";} void reset(){_start=chrono::system_clock::now();sum=0;} private: chrono::system_clock::time_point _start,_end;long long sum=0; }timer; struct Xor128{// period 2^128 - 1 uint32_t x,y,z,w; static constexpr uint32_t min(){return 0;} static constexpr uint32_t max(){return UINT32_MAX;} Xor128(uint32_t seed=0):x(123456789),y(362436069),z(521288629),w(88675123+seed){} uint32_t operator()(){uint32_t t=x^(x<<11);x=y;y=z;z=w;return w=(w^(w>>19))^(t^(t>>8));} uint32_t operator()(uint32_t l,uint32_t r){return((*this)()%(r-l))+l;} uint32_t operator()(uint32_t r){return(*this)()%r;}}; struct Rand{// https://docs.python.org/ja/3/library/random.html Rand(){}; Rand(int seed):gen(seed){}; // シードを変更します inline void set_seed(int seed){Xor128 _gen(seed);gen=_gen;} // ランダムな浮動小数点数(範囲は[0.0, 1.0)) を返します inline double random(){return(double)gen()/(double)gen.max();} // a <= b であれば a <= N <= b 、b < a であれば b <= N <= a であるようなランダムな浮動小数点数 N を返します inline double uniform(double a,double b){if(bvoid shuffle(vector&x){for(int i=x.size(),j;i>1;){j=gen(i);swap(x[j],x[--i]);}} // 空でないシーケンス seq からランダムに要素を返します templateT choice(const vector&seq){assert(!seq.empty());return seq[gen(seq.size())];} // 相対的な重みに基づいて要素が選ばれます (※複数回呼ぶ場合は処理を変えた方が良い) templateT choice(const vector&seq,const vector&weights){assert(seq.size()==weights.size());vectoracc(weights.size());acc[0]=weights[0];for(int i=1;ivectorsample(const vector&p,int k){int j,i=0,n=p.size();assert(0<=k&&k<=n);vectorret(k);unordered_sets;for(;icolors{196,208,226,46,77,14,12,13,5,136,195,245};constexpr int RED=0,ORANGE=1,YELLOW=2,LIGHTGREEN=3,GREEN=4,AQUA=5,BLUE=6,PINK=7,PURPLE=8,BROWN=9,WHITE=10,GRAY=11; void back(int n){cerr<<"\e["<=len)break;ret+=sep;}reverse(ret.begin(),ret.end());return ret;} string with_fill(int n,int num=3,char space=' '){string s=to_string(n);return string(max(0,num-int(s.size())),space)+s;} // **VISUALIZER-TEMPLATE** // int N=20,M=30,vis_length=10; // for(int vis_cnt=1;vis_cnt<=vis_length;vis_cnt++){cerr<> ABs(N, {0, 0}); vector> CDs(M, {0, 0}); inline long long dist(pair& p1, pair& p2) { return (p1.first - p2.first) * (p1.first - p2.first) + (p1.second - p2.second) * (p1.second - p2.second); }; // https://tsunelab-programming.com/alg-kmeans auto k_means() { //分類パターンの属するクラスタ番号 vector iClassNum(N); //各クラスタ中心初期位置 vector> dInitCenterPosit(M); //各クラスタ中心位置 vector> dCenterPosit(M); //合計値 vector> dSum(M, vector(2, 0)); //クラスタに含まれる要素数 vector iElemNum(M, 0); //クラスタ中心位置と分類パターンの距離 vector dTempDistance(M); /* 機能:最小距離クラスの決定 引数:int(クラス分類数) long long*(各クラスと対象パターンの距離) 戻値:最小距離となるクラスの番号 */ auto fiClassDetermination = [&](int iClassNum, vector& pdDist) -> int { long long dTempDist; int iMinNum; int i; //初期値 dTempDist = pdDist[0]; iMinNum = -1; for (i = 0; i < iClassNum; i++) { if (dTempDist >= pdDist[i]) { iMinNum = i; dTempDist = pdDist[i]; } } return iMinNum; }; int i, j; int iEndCheckFlag; while (1) { for (i = 0; i < M; i++) { dInitCenterPosit[i] = dCenterPosit[i] = ABs[myrand.randrange(N)]; } int ok = 1; for (i = 0; i < M; i++) { for (j = i + 1; j < M; j++) { if (dCenterPosit[i] == dCenterPosit[j]) { ok = 0; break; } } } if (ok) { break; } } while (true) { //分類パターンの所属クラスタ決定 for (i = 0; i < N; i++) { //各クラスタとの距離算出 for (j = 0; j < M; j++) { dTempDistance[j] = dist(dCenterPosit[j], ABs[i]); } //最小距離のクラスタ番号取得 iClassNum[i] = fiClassDetermination(M, dTempDistance); } //中心位置修正 for (i = 0; i < N; i++) { assert((iClassNum[i] >= 0) && (iClassNum[i] < M)); iElemNum[iClassNum[i]]++; dSum[iClassNum[i]][0] += ABs[i].first; dSum[iClassNum[i]][1] += ABs[i].second; } for (i = 0; i < M; i++) { if (iElemNum[i]) { dCenterPosit[i].first = dSum[i][0] / (long long)iElemNum[i]; dCenterPosit[i].second = dSum[i][1] / (long long)iElemNum[i]; } } //終了チェック 修正前後での各クラスタ距離算出 iEndCheckFlag = 1; for (i = 0; i < M; i++) { if ((dCenterPosit[i].first - dInitCenterPosit[i].first) * (dCenterPosit[i].first - dInitCenterPosit[i].first) + (dCenterPosit[i].second - dInitCenterPosit[i].second) * (dCenterPosit[i].second - dInitCenterPosit[i].second) >= 1) { iEndCheckFlag = 0; } } if (iEndCheckFlag == 1) { break; } else { for (i = 0; i < M; i++) { dInitCenterPosit[i] = dCenterPosit[i]; } } } return make_pair(iClassNum, dCenterPosit); } // TSP with bitDP (loop) O(N^2 2^N) // 初期値と、dist[v][nv] == -1 の部分などは適宜変更すること // クラスタのセンターの間のtspを解く vector bit_dp(vector>& centers) { int N = centers.size(); vector> dp((1 << N), vector(N, INFll)); for (int i = 1; i < N; i++) { dp[1 << i][i] = dist(centers[0], centers[i]); } for (int bit = 1; bit < (1 << N); bit++) { for (int v = 0; v < N; v++) { if (!(bit & (1 << v))) continue; for (int nv = 0; nv < N; nv++) { if (bit & (1 << nv)) continue; chmin(dp[bit | (1 << nv)][nv], dp[bit][v] + dist(centers[v], centers[nv])); } } } int tsp_ans = dp[(1 << N) - 1][0]; debug(tsp_ans); int now = 0; vector route{now}; int visited_bit = (1 << N) - 1; long long dist_sum = tsp_ans; for (int _ = 0; _ < N - 1; _++) { visited_bit ^= (1 << now); int last_city = 0; for (; last_city < N; last_city++) { if (last_city == now) { continue; } if (dist_sum - dp[visited_bit][last_city] == dist(centers[last_city], centers[now])) { break; } } dist_sum -= dist(centers[last_city], centers[now]); now = last_city; route.push_back(now); } assert(int(route.size()) == N); return route; } int main() { int _N, _M; cin >> _N >> _M; assert(_N == 100 && _M == 8); for (int i = 0; i < N; i++) { int A; int B; cin >> A >> B; ABs[i] = {A, B}; } debug("----"); auto [clast, centers] = k_means(); debug(clast); debug(centers); vector> route; vector centers_route = bit_dp(centers); int return_idx = -1; for (int i = 0; i < M; i++) { route.emplace_back(2, centers_route[i] + 1); for (int j = 0; j < N; j++) { if (clast[j] == centers_route[i]) { route.emplace_back(1, j + 1); if (j == 0) { return_idx = route.size() - 1; } route.emplace_back(2, centers_route[i] + 1); } } } assert(return_idx != -1); rotate(route.begin(), route.begin() + return_idx, route.end()); route.emplace_back(1, 1); // 最初に戻ってくる for (auto& elem : centers) cout << elem.first << " " << elem.second << '\n'; cout << route.size() << endl; for (auto& elem : route) cout << elem.first << " " << elem.second << '\n'; return 0; }