題面:http://acm.zju.edu.cn/contest-materials/qd2018/qd2018_problems.pdfios
題意:spa
n個騎士決鬥K輪code
要求是每一個騎士只能跟另一個騎士決鬥一次blog
每輪必須有 n/2 場決鬥ci
若是在某輪A和B單挑,C和D單挑string
那麼在下面的論場中必然有A和C單挑同時B和D單挑it
思路:io
用一個set存每一個騎士還沒單挑過的其餘騎士編號,一個set存每輪尚未單挑過的騎士class
先預處理第一輪的21436587······pdf
第一個騎士每輪單挑必然是單挑的輪數+1(如第一輪單挑的是2即1+1)
這樣才能知足字典序最小的要求
而後把第一個騎士單挑的第X個騎士的上一個和第一個的上一個進行單挑
而後找本輪還沒單挑的編號最小的騎士去單挑本輪還沒單挑的他還沒單挑過的最小的(好拗口啊ORZ)
代碼上能看的比較清楚,建議直接分析代碼
另外若是k>=lowbit(n) 那麼絕對不可能安排上
具體能夠本身打表看看
代碼(注意這不是最終代碼):
#include <iostream> #include <cstdio> #include <cstring> #include <string> #include <map> #include <set> #include <list> #include <deque> #include <queue> #include <stack> #include <vector> #include <cmath> #include <algorithm> using namespace std; #define it iterator #define ll long long #define eb emplace_back #define lowbit(x) x & -x #define all(x) x.begin(),x.end() #define ZERO(a) memset(a,0,sizeof(a)) #define MINUS(a) memset(a,0xff,sizeof(a)) #define per(x,a,b) for(int x = a; x <= b; x++) #define rep(x,a,b) for(int x = a; x >= b; x--) #define IO ios::sync_with_stdio(false),cin.tie(0),cout.tie(0) const int birth = 19260817; const int mo = 998244353; const int maxn = 1e4 + 10; const int mod = 1e9 + 7; const int INF = 0x3fffffff; const double eps = 1e-8; int ch[maxn][maxn]; //******************THE PROGRAM BEGINING****************** int main() { //cout << (lowbit(12)) << endl; int t; cin >> t; while (t--) { int n, k; set<int> s[maxn]; set<int> e; cin >> n >> k; if (k >= (lowbit(n))) { cout << "Impossible" << endl; continue; } if (lowbit(n) == 2 || k == 1) { per(i, 1, n) { if (i & 1) cout << i + 1; else cout << i - 1; if (i != n) cout << " "; } cout << endl; continue; } else { per(i, 1, n) { if (i & 1) ch[0][i] = i + 1; else { ch[0][i] = i - 1; } per(j, 1, n) { if (i == j || j == ch[0][i]) continue; s[i].insert(j); } } int x = k - 1; int num = 3; int pos = 1; while (x--) { e.clear(); per(i, 2, n) e.insert(i); e.erase(num); ch[pos][1] = num; ch[pos][num] = 1; s[num].erase(1); ch[pos][ch[pos - 1][1]] = ch[pos - 1][num]; e.erase(ch[pos - 1][num]); ch[pos][ch[pos - 1][num]] = ch[pos - 1][1]; e.erase(ch[pos - 1][1]); s[ch[pos - 1][1]].erase(ch[pos - 1][num]); s[ch[pos - 1][num]].erase(ch[pos - 1][1]); while (!e.empty()) { int head = *e.begin(); int tail; e.erase(head); for (set<int>::iterator it = s[head].begin(); it != s[head].end(); it++) { if (e.count(*it)) { tail = *it; break; } } e.erase(tail); ch[pos][head] = tail; ch[pos][tail] = head; s[head].erase(tail); s[tail].erase(head); ch[pos][ch[pos - 1][head]] = ch[pos - 1][tail]; e.erase(ch[pos - 1][head]); ch[pos][ch[pos - 1][tail]] = ch[pos - 1][head]; e.erase(ch[pos - 1][tail]); s[ch[pos - 1][tail]].erase(ch[pos - 1][head]); s[ch[pos - 1][head]].erase(ch[pos - 1][tail]); } num++; pos++; } for (int i = 0; i < k; i++) { for (int j = 1; j <= n; j++) { cout << ch[i][j]; if (j != n) cout << " "; } cout << endl; } } } return 0; }
可是呢,這個方法從時間上講徹底是不夠用的,而後經過上面的進行打表,發現了一個天大的咪咪
#include <iostream> #include <cstdio> #include <algorithm> using namespace std; int ch[1010][1010]; //******************THE PROGRAM BEGINING****************** int main() { int t; cin >> t; while (t--) { int n, k; scanf("%d %d", &n, &k); if (k >= (n & -n)) { puts("Impossible"); continue; } if ((n & -n) == 2 || k == 1) { for(int i = 1; i <= n; i++) { if (i & 1) printf("%d", i + 1); else printf("%d", i - 1); if (i != n) printf(" "); } printf("\n"); continue; } else { for (int i = 1; i <= n; i++) { if (i & 1) { ch[0][i] = i; ch[1][i] = i + 1; } else { ch[0][i] = i; ch[1][i] = i - 1; } } int x = 2; while (x <= k) { for (int i = x; i <= x * 2 - 1; i++) { for (int j = 1; j <= n; j += 2 * x) { for (int k = j; k < j + x; k++) { ch[i][k] = ch[i - x][k + x]; } for (int k = j + x; k < j + 2 * x; k++) { ch[i][k] = ch[i - x][k - x]; } } } x *= 2; } for (int i = 1; i <= k; i++) { for (int j = 1; j <= n; j++) { printf("%d", ch[i][j]); if (j != n) printf(" "); } printf("\n"); } } } return 0; }
其實就是定長翻倍交換左右塊就行了
先打表前兩行
第三第四行爲2*2的塊交換
第五到第八行爲4*4的塊交換
依次推下去便可