F - Tournament(构造)

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();
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值