题面:http://acm.zju.edu.cn/contest-materials/qd2018/qd2018_problems.pdf
题意:
n个骑士决斗K轮
要求是每个骑士只能跟另外一个骑士决斗一次
每轮必须有 n/2 场决斗
如果在某轮A和B单挑,C和D单挑
那么在下面的论场中必然有A和C单挑同时B和D单挑
思路:
用一个set存每个骑士还没单挑过的其他骑士编号,一个set存每轮还没有单挑过的骑士
先预处理第一轮的21436587······
第一个骑士每轮单挑必然是单挑的轮数+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 = ;
const int mo = ;
const int maxn = 1e4 + ;
const int mod = 1e9 + ;
const int INF = 0x3fffffff;
const double eps = 1e-; 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) == || k == )
{
per(i, , n)
{
if (i & )
cout << i + ;
else
cout << i - ;
if (i != n)
cout << " ";
}
cout << endl;
continue;
}
else
{
per(i, , n)
{
if (i & )
ch[][i] = i + ;
else
{
ch[][i] = i - ;
}
per(j, , n)
{
if (i == j || j == ch[][i])
continue;
s[i].insert(j);
}
}
int x = k - ;
int num = ;
int pos = ;
while (x--)
{
e.clear();
per(i, , n)
e.insert(i);
e.erase(num);
ch[pos][] = num;
ch[pos][num] = ;
s[num].erase();
ch[pos][ch[pos - ][]] = ch[pos - ][num];
e.erase(ch[pos - ][num]);
ch[pos][ch[pos - ][num]] = ch[pos - ][];
e.erase(ch[pos - ][]);
s[ch[pos - ][]].erase(ch[pos - ][num]);
s[ch[pos - ][num]].erase(ch[pos - ][]);
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 - ][head]] = ch[pos - ][tail];
e.erase(ch[pos - ][head]);
ch[pos][ch[pos - ][tail]] = ch[pos - ][head];
e.erase(ch[pos - ][tail]);
s[ch[pos - ][tail]].erase(ch[pos - ][head]);
s[ch[pos - ][head]].erase(ch[pos - ][tail]);
}
num++;
pos++;
} for (int i = ; i < k; i++)
{
for (int j = ; j <= n; j++)
{
cout << ch[i][j];
if (j != n)
cout << " ";
}
cout << endl;
}
}
} return ;
}
但是呢,这个方法从时间上讲完全是不够用的,然后通过上面的进行打表,发现了一个天大的咪咪
#include <iostream>
#include <cstdio>
#include <algorithm> using namespace std; int ch[][]; //******************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) == || k == )
{
for(int i = ; i <= n; i++)
{
if (i & )
printf("%d", i + );
else
printf("%d", i - );
if (i != n)
printf(" ");
}
printf("\n");
continue;
}
else
{
for (int i = ; i <= n; i++)
{
if (i & )
{
ch[][i] = i;
ch[][i] = i + ;
}
else
{
ch[][i] = i;
ch[][i] = i - ;
}
} int x = ;
while (x <= k)
{
for (int i = x; i <= x * - ; i++)
{
for (int j = ; j <= n; j += * x)
{
for (int k = j; k < j + x; k++)
{
ch[i][k] = ch[i - x][k + x];
}
for (int k = j + x; k < j + * x; k++)
{
ch[i][k] = ch[i - x][k - x];
}
}
}
x *= ;
} for (int i = ; i <= k; i++)
{
for (int j = ; j <= n; j++)
{
printf("%d", ch[i][j]);
if (j != n)
printf(" ");
}
printf("\n");
}
}
} return ;
}
其实就是定长翻倍交换左右块就好了
先打表前两行
第三第四行为2*2的块交换
第五到第八行为4*4的块交换
依次推下去即可