[UOJ 41]【清华集训2014】矩阵变换

Description

给出一个 $N$ 行 $M$ 列的矩阵A, 保证满足以下性质:

  1. $M > N$。
  2. 矩阵中每个数都是 $[0, N]$ 中的自然数。
  3. 每行中, $[1, N]$ 中每个自然数都恰好出现一次。这意味着每行中 $0$ 恰好出现 $M - N$ 次。
  4. 每列中,$[1, N]$ 中每个自然数至多出现一次。

现在我们要在每行中选取一个非零数,并把这个数之后的数赋值为这个数。我们希望保持上面的性质4,即每列中,$[1, N]$ 中每个自然数仍然至多出现一次。

Input

第一行一个正整数 $T$,表示数据组数。

后面包含 $T$ 组数据,各组数据之间无空行。每组数据以两个正整数 $N, M$ 开始,接下来 $N$ 行,每行 $M$ 个用空格隔开的整数,意义如题所述。

Output

对于每组数据输出一行。如果有解,则输出 $N$ 个整数,依次表示每一行取的数是多少。(这应该是一个 $1$ 到 $N$ 的排列)如果无解,则输出任意卖萌表情。

Sample Input

2
5 10
0 1 0 2 3 0 0 4 0 5
2 0 3 0 0 1 0 5 4 0
4 2 1 0 0 0 3 0 5 0
0 3 0 4 0 5 0 1 2 0
1 0 0 3 2 4 5 0 0 0
5 10
0 1 0 2 3 0 0 4 0 5
2 0 3 0 0 1 0 5 4 0
4 2 1 0 0 0 3 0 5 0
0 3 0 4 0 5 0 1 2 0
1 0 0 3 2 4 5 0 0 0

Sample Output

4 5 3 1 2
5 4 3 1 2

Sample Explanation

两组输入数据是相同的。由于结果不唯一,你可以给出任意一组合法答案。

Hint

对于 20% 的数据,$M < 8, T < 8$。

对于 40% 的数据,$N < 8, T < 8$。

对于 100% 的数据,$N < 200, M < 400, T < 50$。

卖萌表情包括但不限于“\(^o^)/” (不含引号).

由于输入数据较大, 请自行优化输入方法.

时间限制:$1\texttt{s}$

空间限制:$512\texttt{MB}$

题解

稳定婚姻问题。

首先值得肯定的是每一行所选的数不能相同,那么现在就相当于 $n$ 个行匹配 $n$ 个数。

那么什么是不稳定的婚姻?如果假设数 $x$ 在第 $i$ 行选中的数之前(确保 $x$ 没有被选中的数删去),又同时选中 $x$ 的 $j$ 行中 $x$ 位置比 $i$ 行中 $x$ 位置靠前。显然这个时候是不合法的。但只要我们选第 $i$ 行的 $x$ ,再作调整就能得到合法的解。

所以归纳得出的结论就是:在一行中,偏好选在靠前面的数字。而对于每个数字,偏好其在行中的位置靠后的行。

那么就可以跑 $Gale-Shapley$ 。

 1 //It is made by Awson on 2018.1.18
 2 #include <set>
 3 #include <map>
 4 #include <cmath>
 5 #include <ctime>
 6 #include <queue>
 7 #include <stack>
 8 #include <cstdio>
 9 #include <string>
10 #include <vector>
11 #include <cstdlib>
12 #include <cstring>
13 #include <iostream>
14 #include <algorithm>
15 #define LL long long
16 #define Abs(a) ((a) < 0 ? (-(a)) : (a))
17 #define Max(a, b) ((a) > (b) ? (a) : (b))
18 #define Min(a, b) ((a) < (b) ? (a) : (b))
19 #define Swap(a, b) ((a) ^= (b), (b) ^= (a), (a) ^= (b))
20 #define writeln(x) (write(x), putchar('\n'))
21 using namespace std;
22 const int N = 200;
23 void read(int &x) {
24     char ch; bool flag = 0;
25     for (ch = getchar(); !isdigit(ch) && ((flag |= (ch == '-')) || 1); ch = getchar());
26     for (x = 0; isdigit(ch); x = (x<<1)+(x<<3)+ch-48, ch = getchar());
27     x *= 1-2*flag;
28 }
29 void write(int x) {
30     if (x > 9) write(x/10);
31     putchar(x%10+48);
32 }
33 
34 int n, m;
35 int alike[N+5][N+5], blike[N+5][N+5];
36 int achoice[N+5], bchoice[N+5];
37 queue<int>Q;
38 
39 void work() {
40     read(n), read(m);
41     for (int i = 1; i <= n; i++)
42     for (int j = 1, tot = 0, x = 0; j <= m; j++, x = 0) {
43         read(x); if (x) alike[i][++tot] = x, blike[x][i] = j;
44     }
45     for (int i = 1; i <= n; i++) blike[i][n+1] = 0, bchoice[i] = n+1, achoice[i] = 1, Q.push(i);
46     while (!Q.empty()) {
47     int a = Q.front(), b = alike[a][achoice[a]];
48     if (blike[b][bchoice[b]] < blike[b][a]) {
49         Q.pop();
50         if (bchoice[b] != n+1) {
51         achoice[bchoice[b]]++;
52         Q.push(bchoice[b]);
53         }
54         bchoice[b] = a;
55     }else achoice[a]++;
56     }
57     for (int i = 1; i <= n; i++) write(alike[i][achoice[i]]), putchar(' ');
58 }
59 int main() {
60     int t; read(t);
61     while (t--) {work(); if (t) putchar('\n'); }
62     return 0;
63 }

 

posted @ 2018-01-18 08:36  NaVi_Awson  阅读(409)  评论(0编辑  收藏  举报