题目链接:传送门
假思路:
根据题意要求,只能按字典序最小的方法安排比赛。
所以第一场必定是1和2比,3和4比。。。。
选手:1 2
对手:2 1
根据要求如果1与2比过赛了,1再与其它的人(不妨设为a)比赛的话,2就必须与(第一场与a比赛的人,不妨设为b)比赛。
因为要求字典序小,所以1应该和a、b中较小者先比赛,再与较大者比赛,那么不妨令a < b,则1、2、a、b比赛时就会是这样安排的:
选手:1 2 a b
对手:2 1 b a
a b 1 2
b a 2 1
因为要求字典序最小,所以a、b应当先与3、4比赛:
选手:1 2 3 4
对手:2 1 4 3
3 4 1 2
4 3 2 1
(然后陈某人脑子一片空白打了个表。。。。验证一下能不能比k轮就交上去了。)
同样地,1已经与2、3、4比过赛了,那么它再碰见a、b、c、d(不妨令a < b < c < d)时,就会因为字典序要求依次与a、b、c、d比赛。(a、b、c、d取到最小,所以对应了5、6、7、8)
选手:1 2 3 4 a b c d
对手:2 1 4 3 b a d c
3 4 1 2 c d a b
4 3 2 1 d c b a
a b c d 1 2 3 4
b a d c 2 1 4 3
c d a b 3 4 1 2
d c b a 4 3 2 1
这样安排首先能保证字典序最小和满足第四个规则,其次也能使得比赛的轮数尽量多(较大的数与前面的数比赛的时间更迟),所以答案就在表里。验证表中的前k行的前n个数是否出现了比n大的数即可判断是否为Impossible。
代码:
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int MAX_N = 1e3 + 50; int N, K; int mat[MAX_N][MAX_N]; void work(int len) { if (2*len > MAX_N) return; for (int i = 1; i <= len; i++) { for (int j = 1; j <= len; j++) { mat[i+len][j] = mat[i][j] + len; mat[i+len][j+len] = mat[i][j]; mat[i][j+len] = mat[i][j] + len; } } work(2*len); } void init() { mat[1][1] = 1; mat[1][2] = 2; mat[2][1] = 2; mat[2][2] = 1; work(2); } bool check() { for (int i = 2; i <= K+1; i++) { for (int j = 1; j <= N; j++) { if (mat[i][j] > N) { return false; } } } return true; } int main() { init(); int T; cin >> T; while (T--) { scanf("%d%d", &N, &K); bool ok = check(); if (!ok) { puts("Impossible"); continue; } for (int i = 2; i <= K+1; i++) { bool firstprint = true; for (int j = 1; j <= N; j++) { if (firstprint) firstprint = false; else printf(" "); printf("%d", mat[i][j]); } puts(""); } } }