Description
给出一个 $N$ 行 $M$ 列的矩阵A, 保证满足以下性质:
- $M > N$。
- 矩阵中每个数都是 $[0, N]$ 中的自然数。
- 每行中, $[1, N]$ 中每个自然数都恰好出现一次。这意味着每行中 $0$ 恰好出现 $M - N$ 次。
- 每列中,$[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 exttt{s}$
空间限制:$512 exttt{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(' ')) 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(' '); } 62 return 0; 63 }