Submission #63893180


Source Code Expand

/*
	[解法の説明]
	1. 基本的な方針
	- 社員 i にちょうど T[i] 回掃除当番が回ってくると仮定する
	- このとき、社員 a[i], b[i] に T[i]/2 回掃除当番が渡る、と考えらえる
	- したがって、(a[j], b[j] = i となるものに対する T[j]/2 の合計) ができるだけ T[i] に近くなれば良い
	- この "誤差" をできるだけ小さくする a[i], b[i] を決めることを考える
	2. "ビンパッキング問題" のような問題としてとらえる
	- 結局 1. で述べた問題は以下の問題になる
	  - N 個のビンがあって、それぞれ容量 T[0], T[1], ..., T[N-1] である
	  - アイテムが 2N 個あって、それぞれサイズ T[0]/2, T[0]/2, T[1]/2, T[1]/2, ..., T[N-1]/2, T[N-1]/2 である
	  - すべてのアイテムをビンに入れるとき、各ビンの |容量 - 入れたアイテムのサイズの合計| の総和を最小化する
	- これだと、ビン i にサイズ T[i]/2, T[i]/2 のアイテムを入れるのが最適になる
	  - しかし、この解では社員 0 がずっと掃除当番をやることになってしまう
	  - そのため、ビン i にはサイズ T[i]/2 のアイテムを入れてはいけない、という制約を入れた問題を解くことにする
	3. 山登り法で最適化する
	- 適当な初期解から始める
	- 以下の状態遷移を行って解の改善を試みる
	  - 2 つのビンを選び、その中のアイテムを組み替える、という変更を行う
	  - 誤差がより小さくなったら、変更を採用する
	- 改善できなくなる or 実行時間制限を過ぎる まで改善を繰り返す
	- 各ビンには平均 2 個程度しかアイテムがないので、組み替えの方法は全探索できる
	- 平均計算量 O(1) で改善できるかが判定でき、スコアを平均計算量 O(1) で差分更新できる
	4. 山登り法の工夫
	- 焼きなまし法をするとできるが、ここでは別のアプローチを考える
	- 反復局所探索法のアプローチ
	  [1] 2 つのビンの組を全探索して、その中で改善できるものを探す
	  [2] N(N-1)/2 通りのどれでも誤差が改善しなかったら (局所解)、以下の kick 操作を行う
	    - アイテムを 1 つランダムに選んで、それを別のビンに移し替える
	  [3] 局所解に到達するたびに実際のスコアを計算する
	- 時には誤差と比べて実際のスコアが低くなることがあるが、これは手順 [3] で実際のスコアを何回も計算するので対処できる
	- 手順 [1] から [3] までのループは、実行時間制限 2 秒では 100 回程度回る
	- 単純な誤差ではなく、2 乗誤差を最小化する
*/

#include <bits/stdc++.h>
using namespace std;

const int INF = 1012345678;

namespace myrandom {
	uint64_t seed = 1234567891234567891;
	uint64_t xorshift64() {
		seed ^= seed << 13;
		seed ^= seed >> 7;
		seed ^= seed << 17;
		return seed;
	}
	// l 以上 r 未満のランダムな整数を返す関数
	int64_t next_int(int64_t l, int64_t r) {
		int64_t z = xorshift64() % (r - l);
		return l + z;
	}
};

// 社員を表す構造体: a[i], b[i] を表す
class person {
public:
	int a, b;
	person() : a(-1), b(-1) {}
	person(int a_, int b_) : a(a_), b(b_) {}
};

// 求めた答えのスコアを計算する関数
int get_score(int N, int L, const vector<int>& T, const vector<person>& plan) {
	vector<int> count(N, 0);
	int cur = 0;
	for (int i = 0; i < L; i++) {
		if (count[cur] % 2 == 0) {
			cur = plan[cur].a;
		} else {
			cur = plan[cur].b;
		}
		count[cur] += 1;
	}
	int score = 2 * L;
	for (int i = 0; i < N; i++) {
		score -= abs(count[i] - T[i]);
	}
	return score;
}

vector<person> solver(int N, int L, const vector<int> T) {
	// step #1. ビンに入れるアイテムの大きさを計算
	vector<int> X(N * 2);
	for (int i = 0; i < N; i++) {
		X[i * 2 + 0] = (T[i] + 1) / 2;
		X[i * 2 + 1] = (T[i] + 0) / 2;
	}

	// step #2. 山登り法の準備
	const int CAPACITY = 5;
	vector<int> G(N * 2);
	vector<int> C(N);
	vector<int> S(N);
	vector<array<int, CAPACITY> > GL(N);
	for (int i = 0; i < N; i++) {
		fill(GL[i].begin(), GL[i].end(), -1);
	}
	for (int i = 0; i < N * 2; i++) {
		do {
			G[i] = myrandom::next_int(0, N);
		} while (G[i] == i / 2 || C[G[i]] == CAPACITY);
		S[G[i]] += X[i];
		GL[G[i]][C[G[i]]++] = i;
	}

	// step #3. ビン a, b を組み替えて改善できるかを全探索で判定する関数
	auto mixing = [&](int a, int b) -> bool {
		vector<int> V;
		V.reserve(C[a] + C[b]);
		int base_ca = 0, base_sa = 0;
		for (int i = 0; i < C[a]; i++) {
			if (GL[a][i] / 2 == b) {
				base_ca++;
				base_sa += X[GL[a][i]];
			} else {
				V.push_back(GL[a][i]);
			}
		}
		int base_cb = 0, base_sb = 0;
		for (int i = 0; i < C[b]; i++) {
			if (GL[b][i] / 2 == a) {
				base_cb++;
				base_sb += X[GL[b][i]];
			} else {
				V.push_back(GL[b][i]);
			}
		}
		int VS = V.size();
		vector<int> P(1 << VS);
		for (int i = 0; i < VS; i++) {
			for (int j = 0; j < (1 << i); j++) {
				P[j | (1 << i)] = P[j] + X[V[i]];
			}
		}
		int total = P[(1 << VS) - 1];
		pair<int, int> opt = {(S[a] - T[a]) * (S[a] - T[a]) + (S[b] - T[b]) * (S[b] - T[b]), -1};
		for (int i = 0; i < (1 << VS); i++) {
			int ca = base_ca + __builtin_popcount(i);
			int cb = base_cb + (VS - __builtin_popcount(i));
			if (ca <= CAPACITY && cb <= CAPACITY) {
				int sa = base_sa + P[i];
				int sb = base_sb + (total - P[i]);
				int loss = (sa - T[a]) * (sa - T[a]) + (sb - T[b]) * (sb - T[b]);
				opt = min(opt, {loss, i});
			}
		}
		if (opt.second == -1) {
			return false;
		}
		array<int, CAPACITY> gla, glb;
		fill(gla.begin(), gla.end(), -1);
		fill(glb.begin(), glb.end(), -1);
		int nca = 0;
		for (int i = 0; i < C[a]; i++) {
			if (GL[a][i] / 2 == b) {
				gla[nca++] = GL[a][i];
			}
		}
		int ncb = 0;
		for (int i = 0; i < C[b]; i++) {
			if (GL[b][i] / 2 == a) {
				glb[ncb++] = GL[b][i];
			}
		}
		for (int i = 0; i < VS; i++) {
			if ((opt.second >> i) & 1) {
				gla[nca++] = V[i];
				G[V[i]] = a;
			} else {
				glb[ncb++] = V[i];
				G[V[i]] = b;
			}
		}
		C[a] = nca;
		C[b] = ncb;
		GL[a] = gla;
		GL[b] = glb;
		S[a] = 0;
		for (int i = 0; i < nca; i++) {
			S[a] += X[gla[i]];
		}
		S[b] = 0;
		for (int i = 0; i < ncb; i++) {
			S[b] += X[glb[i]];
		}
		return true;
	};

	// step #4. 2 つのビンの組を全探索して、改善できるかを判定する関数
	auto find_improvement = [&]() -> bool {
		for (int i = 0; i < N; i++) {
			for (int j = i + 1; j < N; j++) {
				if (abs(S[i] - T[i]) + abs(S[j] - T[j]) >= 2 && mixing(i, j)) {
					return true;
				}
			}
		}
		return false;
	};

	// step #5. 山登り法のメイン部分
	const double TIME_LIMIT = 1.9;
	time_t start_clock = clock();
	double time_ratio = 0.0;
	int best_score = -INF;
	vector<person> best_ans;
	int64_t loops = 0;
	while (true) {
		time_t cur_clock = clock();
		time_ratio = (cur_clock - start_clock) / (TIME_LIMIT * CLOCKS_PER_SEC);
		if (time_ratio >= 1.0) {
			break;
		}
		loops++;
		int64_t subloops = 0;
		while (find_improvement()) {
			subloops++;
		}
		int loss = 0;
		for (int i = 0; i < N; i++) {
			loss += abs(S[i] - T[i]);
		}
		vector<person> ans(N);
		for (int i = 0; i < N; i++) {
			ans[i] = person(G[i * 2 + 0], G[i * 2 + 1]);
		}
		int score = get_score(N, L, T, ans);
		cerr << "loop #" << loops << ": loss = " << loss << ", score = " << score << endl;	
		if (best_score < score) {
			best_score = score;
			best_ans = ans;
		}
		int a = myrandom::next_int(0, 2 * N);
		int b;
		do {
			b = myrandom::next_int(0, N);
		} while (b == G[a] || b == a / 2 || C[b] == CAPACITY);
		for (int i = 0; i < C[G[a]] - 1; i++) {
			if (GL[G[a]][i] == a) {
				GL[G[a]][i] = GL[G[a]][C[G[a]] - 1];
				GL[G[a]][C[G[a]] - 1] = -1;
				break;
			}
		}
		C[G[a]]--;
		S[G[a]] -= X[a];
		G[a] = b;
		GL[b][C[b]] = a;
		C[b]++;
		S[b] += X[a];
	}

	// 求めた答えを返す
	return best_ans;
}

int main() {
	// 入力
	int N, L;
	cin >> N >> L;
	vector<int> T(N);
	for (int i = 0; i < N; i++) {
		cin >> T[i];
	}

	// 答えを求める
	vector<person> ans = solver(N, L, T);

	// 出力
	for (int i = 0; i < N; i++) {
		cout << ans[i].a << ' ' << ans[i].b << endl;
	}

	return 0;
}

Submission Info

Submission Time
Task A - Cleaning Up
User square1001
Language C++ 20 (gcc 12.2)
Score 149870096
Code Size 8603 Byte
Status AC
Exec Time 1956 ms
Memory 3768 KiB

Judge Result

Set Name test_ALL
Score / Max Score 149870096 / 150000000
Status
AC × 150
Set Name Test Cases
test_ALL test_0000.txt, test_0001.txt, test_0002.txt, test_0003.txt, test_0004.txt, test_0005.txt, test_0006.txt, test_0007.txt, test_0008.txt, test_0009.txt, test_0010.txt, test_0011.txt, test_0012.txt, test_0013.txt, test_0014.txt, test_0015.txt, test_0016.txt, test_0017.txt, test_0018.txt, test_0019.txt, test_0020.txt, test_0021.txt, test_0022.txt, test_0023.txt, test_0024.txt, test_0025.txt, test_0026.txt, test_0027.txt, test_0028.txt, test_0029.txt, test_0030.txt, test_0031.txt, test_0032.txt, test_0033.txt, test_0034.txt, test_0035.txt, test_0036.txt, test_0037.txt, test_0038.txt, test_0039.txt, test_0040.txt, test_0041.txt, test_0042.txt, test_0043.txt, test_0044.txt, test_0045.txt, test_0046.txt, test_0047.txt, test_0048.txt, test_0049.txt, test_0050.txt, test_0051.txt, test_0052.txt, test_0053.txt, test_0054.txt, test_0055.txt, test_0056.txt, test_0057.txt, test_0058.txt, test_0059.txt, test_0060.txt, test_0061.txt, test_0062.txt, test_0063.txt, test_0064.txt, test_0065.txt, test_0066.txt, test_0067.txt, test_0068.txt, test_0069.txt, test_0070.txt, test_0071.txt, test_0072.txt, test_0073.txt, test_0074.txt, test_0075.txt, test_0076.txt, test_0077.txt, test_0078.txt, test_0079.txt, test_0080.txt, test_0081.txt, test_0082.txt, test_0083.txt, test_0084.txt, test_0085.txt, test_0086.txt, test_0087.txt, test_0088.txt, test_0089.txt, test_0090.txt, test_0091.txt, test_0092.txt, test_0093.txt, test_0094.txt, test_0095.txt, test_0096.txt, test_0097.txt, test_0098.txt, test_0099.txt, test_0100.txt, test_0101.txt, test_0102.txt, test_0103.txt, test_0104.txt, test_0105.txt, test_0106.txt, test_0107.txt, test_0108.txt, test_0109.txt, test_0110.txt, test_0111.txt, test_0112.txt, test_0113.txt, test_0114.txt, test_0115.txt, test_0116.txt, test_0117.txt, test_0118.txt, test_0119.txt, test_0120.txt, test_0121.txt, test_0122.txt, test_0123.txt, test_0124.txt, test_0125.txt, test_0126.txt, test_0127.txt, test_0128.txt, test_0129.txt, test_0130.txt, test_0131.txt, test_0132.txt, test_0133.txt, test_0134.txt, test_0135.txt, test_0136.txt, test_0137.txt, test_0138.txt, test_0139.txt, test_0140.txt, test_0141.txt, test_0142.txt, test_0143.txt, test_0144.txt, test_0145.txt, test_0146.txt, test_0147.txt, test_0148.txt, test_0149.txt
Case Name Status Exec Time Memory
test_0000.txt AC 1914 ms 3620 KiB
test_0001.txt AC 1914 ms 3612 KiB
test_0002.txt AC 1916 ms 3488 KiB
test_0003.txt AC 1916 ms 3764 KiB
test_0004.txt AC 1912 ms 3584 KiB
test_0005.txt AC 1910 ms 3572 KiB
test_0006.txt AC 1921 ms 3616 KiB
test_0007.txt AC 1917 ms 3528 KiB
test_0008.txt AC 1903 ms 3528 KiB
test_0009.txt AC 1921 ms 3528 KiB
test_0010.txt AC 1909 ms 3764 KiB
test_0011.txt AC 1910 ms 3488 KiB
test_0012.txt AC 1911 ms 3668 KiB
test_0013.txt AC 1931 ms 3528 KiB
test_0014.txt AC 1923 ms 3596 KiB
test_0015.txt AC 1919 ms 3640 KiB
test_0016.txt AC 1908 ms 3588 KiB
test_0017.txt AC 1908 ms 3484 KiB
test_0018.txt AC 1912 ms 3596 KiB
test_0019.txt AC 1908 ms 3608 KiB
test_0020.txt AC 1920 ms 3632 KiB
test_0021.txt AC 1956 ms 3580 KiB
test_0022.txt AC 1914 ms 3604 KiB
test_0023.txt AC 1922 ms 3528 KiB
test_0024.txt AC 1921 ms 3636 KiB
test_0025.txt AC 1918 ms 3624 KiB
test_0026.txt AC 1917 ms 3524 KiB
test_0027.txt AC 1916 ms 3560 KiB
test_0028.txt AC 1908 ms 3576 KiB
test_0029.txt AC 1914 ms 3584 KiB
test_0030.txt AC 1906 ms 3528 KiB
test_0031.txt AC 1911 ms 3760 KiB
test_0032.txt AC 1907 ms 3628 KiB
test_0033.txt AC 1902 ms 3616 KiB
test_0034.txt AC 1917 ms 3640 KiB
test_0035.txt AC 1909 ms 3488 KiB
test_0036.txt AC 1932 ms 3528 KiB
test_0037.txt AC 1910 ms 3644 KiB
test_0038.txt AC 1913 ms 3648 KiB
test_0039.txt AC 1910 ms 3560 KiB
test_0040.txt AC 1915 ms 3628 KiB
test_0041.txt AC 1919 ms 3636 KiB
test_0042.txt AC 1909 ms 3672 KiB
test_0043.txt AC 1916 ms 3596 KiB
test_0044.txt AC 1904 ms 3488 KiB
test_0045.txt AC 1941 ms 3488 KiB
test_0046.txt AC 1915 ms 3616 KiB
test_0047.txt AC 1916 ms 3588 KiB
test_0048.txt AC 1920 ms 3580 KiB
test_0049.txt AC 1908 ms 3632 KiB
test_0050.txt AC 1911 ms 3608 KiB
test_0051.txt AC 1915 ms 3524 KiB
test_0052.txt AC 1910 ms 3620 KiB
test_0053.txt AC 1904 ms 3628 KiB
test_0054.txt AC 1915 ms 3604 KiB
test_0055.txt AC 1905 ms 3636 KiB
test_0056.txt AC 1906 ms 3584 KiB
test_0057.txt AC 1913 ms 3564 KiB
test_0058.txt AC 1907 ms 3592 KiB
test_0059.txt AC 1921 ms 3604 KiB
test_0060.txt AC 1916 ms 3604 KiB
test_0061.txt AC 1905 ms 3580 KiB
test_0062.txt AC 1911 ms 3528 KiB
test_0063.txt AC 1916 ms 3632 KiB
test_0064.txt AC 1911 ms 3692 KiB
test_0065.txt AC 1907 ms 3668 KiB
test_0066.txt AC 1903 ms 3584 KiB
test_0067.txt AC 1914 ms 3484 KiB
test_0068.txt AC 1906 ms 3612 KiB
test_0069.txt AC 1921 ms 3580 KiB
test_0070.txt AC 1917 ms 3632 KiB
test_0071.txt AC 1904 ms 3760 KiB
test_0072.txt AC 1908 ms 3588 KiB
test_0073.txt AC 1913 ms 3668 KiB
test_0074.txt AC 1907 ms 3488 KiB
test_0075.txt AC 1903 ms 3528 KiB
test_0076.txt AC 1918 ms 3604 KiB
test_0077.txt AC 1908 ms 3648 KiB
test_0078.txt AC 1915 ms 3584 KiB
test_0079.txt AC 1913 ms 3616 KiB
test_0080.txt AC 1918 ms 3632 KiB
test_0081.txt AC 1915 ms 3524 KiB
test_0082.txt AC 1924 ms 3628 KiB
test_0083.txt AC 1918 ms 3628 KiB
test_0084.txt AC 1933 ms 3488 KiB
test_0085.txt AC 1915 ms 3524 KiB
test_0086.txt AC 1926 ms 3764 KiB
test_0087.txt AC 1927 ms 3688 KiB
test_0088.txt AC 1924 ms 3644 KiB
test_0089.txt AC 1913 ms 3604 KiB
test_0090.txt AC 1914 ms 3632 KiB
test_0091.txt AC 1908 ms 3608 KiB
test_0092.txt AC 1915 ms 3764 KiB
test_0093.txt AC 1908 ms 3632 KiB
test_0094.txt AC 1904 ms 3768 KiB
test_0095.txt AC 1937 ms 3628 KiB
test_0096.txt AC 1922 ms 3628 KiB
test_0097.txt AC 1909 ms 3564 KiB
test_0098.txt AC 1920 ms 3764 KiB
test_0099.txt AC 1908 ms 3620 KiB
test_0100.txt AC 1915 ms 3560 KiB
test_0101.txt AC 1906 ms 3768 KiB
test_0102.txt AC 1917 ms 3608 KiB
test_0103.txt AC 1912 ms 3692 KiB
test_0104.txt AC 1924 ms 3764 KiB
test_0105.txt AC 1912 ms 3768 KiB
test_0106.txt AC 1909 ms 3768 KiB
test_0107.txt AC 1911 ms 3588 KiB
test_0108.txt AC 1917 ms 3576 KiB
test_0109.txt AC 1914 ms 3632 KiB
test_0110.txt AC 1907 ms 3556 KiB
test_0111.txt AC 1918 ms 3764 KiB
test_0112.txt AC 1906 ms 3576 KiB
test_0113.txt AC 1924 ms 3584 KiB
test_0114.txt AC 1910 ms 3684 KiB
test_0115.txt AC 1907 ms 3580 KiB
test_0116.txt AC 1909 ms 3572 KiB
test_0117.txt AC 1913 ms 3668 KiB
test_0118.txt AC 1920 ms 3632 KiB
test_0119.txt AC 1918 ms 3592 KiB
test_0120.txt AC 1914 ms 3628 KiB
test_0121.txt AC 1912 ms 3688 KiB
test_0122.txt AC 1920 ms 3520 KiB
test_0123.txt AC 1914 ms 3764 KiB
test_0124.txt AC 1905 ms 3632 KiB
test_0125.txt AC 1925 ms 3632 KiB
test_0126.txt AC 1904 ms 3632 KiB
test_0127.txt AC 1908 ms 3672 KiB
test_0128.txt AC 1919 ms 3592 KiB
test_0129.txt AC 1915 ms 3592 KiB
test_0130.txt AC 1909 ms 3636 KiB
test_0131.txt AC 1903 ms 3580 KiB
test_0132.txt AC 1916 ms 3608 KiB
test_0133.txt AC 1903 ms 3488 KiB
test_0134.txt AC 1910 ms 3632 KiB
test_0135.txt AC 1903 ms 3616 KiB
test_0136.txt AC 1905 ms 3628 KiB
test_0137.txt AC 1913 ms 3600 KiB
test_0138.txt AC 1919 ms 3636 KiB
test_0139.txt AC 1909 ms 3588 KiB
test_0140.txt AC 1913 ms 3608 KiB
test_0141.txt AC 1909 ms 3636 KiB
test_0142.txt AC 1915 ms 3528 KiB
test_0143.txt AC 1903 ms 3632 KiB
test_0144.txt AC 1914 ms 3648 KiB
test_0145.txt AC 1906 ms 3616 KiB
test_0146.txt AC 1925 ms 3604 KiB
test_0147.txt AC 1913 ms 3524 KiB
test_0148.txt AC 1915 ms 3572 KiB
test_0149.txt AC 1905 ms 3672 KiB