Tournament - ZOJ 4063 - Virtual Judge (vjudge.net)
题目要求字典序最小。并且o(n^2)时间复杂度。
可以dfs暴力打表找出规律或者在纸上写也可以,n=16的时候是这样子的
设最初的第0行是1到n
可以发现他们之间的每一行由上一行而来的变化规律,当k为奇数的时候,相邻之间两两交换,把当k为2的时候,第一个元素和第四个元素交换,第二个元素和三个元素交换,当k为4的时候,第一个元素和第八个元素交换,第二个元素和第七个元素交换..可以发现是由区间长度从外向内交换的。
把n扩大到32,列出他们的行数和所需交换的区间长度之间的关系:
可以发现第n行所需交换的区间长度跟n的关系只跟n二进制下的最小位的1有关系.
也就是第n行所需交换的区间长度=对n取lowbit操作*2
可以发现这个性质对行数为奇数时也成立,因为奇数的最低位1就是最后一个。
知道原理代码就好实现了,我们创建一个二维mp数组,对第0行赋值1-n,后面每一行都由上一行交换得来。
那么如何判断输出-1呢?我们初始化mp数组全为-1,当所有的交换操作进行完成之后发现数组内还有-1的时候就说明有违法操作,如越界,某些区块未交换等,说明是一组不符合题意的n和k,输出Impossible(这里看题解发现一个更优的判断方法就是k > lowbit(n) - 1时输出-1,当然这个结论我没想到)
附上代码:
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
const int N = 1e3 + 10;
const int mod = 10000007;
#define lowbit(x) ((-x) & x)
int mp[N][N];
int n, k;
void solve()
{
cin >> n >> k;
// if (k > lowbit(n) - 1)
// {
// cout << "Impossible" << endl;
// return;
// }
// 题解结论,可以直接用来判断Impossible
memset(mp, -1, sizeof mp);
for (int i = 1; i <= n; i++)
mp[0][i] = i;
for (int i = 1; i <= k; i++)
{
int len = lowbit(i) << 1; // 区块的长度是对i取lowbit操作*2
for (int j = 1; j <= n; j += len)
{
int l = j;
int r = j + len - 1;
while (l < r) // 对于每个区块,从外开始向内交换
{
mp[i][l] = mp[i - 1][r];
mp[i][r] = mp[i - 1][l];
l++;
r--;
}
}
}
for (int i = 1; i <= k; i++)
{
for (int j = 1; j <= n; j++)
{
if (mp[i][j] == -1) // 如果==-1,说明有违法操作,如越界,某些区块未交换等,说明是一组不符合题意的n和k,输出Impossible
{
cout << "Impossible" << endl;
return;
}
}
}
for (int i = 1; i <= k; i++)
{
cout << mp[i][1];
for (int j = 2; j <= n; j++)
{
cout << ' ' << mp[i][j];
}
cout << endl;
}
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t;
cin >> t;
while (t--)
solve();
}