[关闭]
@Dmaxiya 2022-12-04T22:23:20.000000Z 字数 6116 阅读 237

移花接木题解

出题


通过观察可以发现行与列的操作互不影响,都不会改变两个元素在矩阵内同一行 / 列的相对关系,可以分别对行、列操作进行统计。

可以通过 暴力枚举所有可能的交换情况,然后对每种情况进行校验。

每行内若使用双重 for 循环校验时间复杂度为:,若检查每个数的倍数是否在当前行内时间复杂度为 ,由于全排列只能通过 的数据,因此使用 的最坏时间复杂度更小。

每列内只要记录上一次出现的非 值是否小于当前位置上的数字,就可以 次完成校验。

总时间复杂度为 ,可通过 的数据。


一、考虑到交换列时同一列内的元素相互绑定,在每一行内,必须保证一个数的约数在其之前,倍数在其之后,即某些列之间的前后关系是确定的,而另一些不能完全确定,最终需要将这些列按从 的顺序进行排序,若将每一列视为一个点,约数与倍数的相对关系就是一个有向图,判断能否在无限次交换内满足目标条件,可使用拓扑排序判断有向图内是否存在环,若存在环输出 no,不存在输出 yes。交换行的做法同理不再赘述。

二、如何快速找到有向边需要指向的数字,对于交换行而言,只需要将每列的数字取出排序,然后将相邻的两个非零数字连边即可。对于交换列而言,每行内的处理方式为:将所有数字放到一个集合中,跑到每一个正整数 时,判断其 倍是否存在于集合中,若存在则将两个数字所在列连一条有向边。

时间复杂度的计算:第一步拓扑排序时间复杂度是 ,第二步每列内排序时间复杂度是 ,每行内的最坏情况是 均匀分布在同一行,其时间复杂度约为:


由于一行最多只有 个数据且较为随机,因此实际上达不到 ,加上 组数据,最终总的时间复杂度为 ,可通过 的数据。

暴力解法

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. typedef long long LL;
  4. const int maxn = 100;
  5. int T, n, m;
  6. bool ans;
  7. int nums[maxn][maxn], idx[maxn];
  8. bool judgeRow() {
  9. for (int j = 0; j < m; ++j) {
  10. int last = 0;
  11. for (int i = 0; i < n; ++i) {
  12. if (nums[idx[i]][j] == 0) {
  13. continue;
  14. }
  15. if (nums[idx[i]][j] < last) {
  16. return false;
  17. }
  18. last = nums[idx[i]][j];
  19. }
  20. }
  21. return true;
  22. }
  23. bool judgeCol() {
  24. for (int i = 0; i < n; ++i) {
  25. for (int j = 0; j < m; ++j) {
  26. if (nums[i][idx[j]] == 0) {
  27. continue;
  28. }
  29. for (int k = j + 1; k < m; ++k) {
  30. if (nums[i][idx[k]] == 0) {
  31. continue;
  32. }
  33. if (nums[i][idx[j]] % nums[i][idx[k]] == 0) {
  34. return false;
  35. }
  36. }
  37. }
  38. }
  39. return true;
  40. }
  41. int main() {
  42. scanf("%d", &T);
  43. while (T--) {
  44. scanf("%d%d", &n, &m);
  45. for (int i = 0; i < n; ++i) {
  46. for (int j = 0; j < m; ++j) {
  47. scanf("%d", &nums[i][j]);
  48. }
  49. }
  50. for (int i = 0; i < n; ++i) {
  51. idx[i] = i;
  52. }
  53. ans = false;
  54. do {
  55. if (judgeRow()) {
  56. ans = true;
  57. break;
  58. }
  59. } while (next_permutation(idx, idx + n));
  60. if (!ans) {
  61. printf("no\n");
  62. continue;
  63. }
  64. for (int j = 0; j < m; ++j) {
  65. idx[j] = j;
  66. }
  67. ans = false;
  68. do {
  69. if (judgeCol()) {
  70. ans = true;
  71. break;
  72. }
  73. } while (next_permutation(idx, idx + m));
  74. if (ans) {
  75. printf("yes\n");
  76. } else {
  77. printf("no\n");
  78. }
  79. }
  80. return 0;
  81. }

标程

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. typedef long long LL;
  4. const int maxn = 1000000 + 100;
  5. const int maxnm = 100000 + 100;
  6. int T, n, m;
  7. bool ans;
  8. int nums[maxnm], degIn[maxnm], numsTmp[maxnm];
  9. int idxOfNum[maxn];
  10. int vis[maxn];
  11. vector<int> G[maxnm];
  12. queue<int> que;
  13. inline int id(int x, int y) {
  14. return x * m + y;
  15. }
  16. bool topsort(int n) {
  17. while (!que.empty()) {
  18. que.pop();
  19. }
  20. for (int i = 0; i < n; ++i) {
  21. if (degIn[i] == 0) {
  22. que.push(i);
  23. }
  24. }
  25. while (!que.empty()) {
  26. int tmp = que.front();
  27. que.pop();
  28. for (auto pos: G[tmp]) {
  29. --degIn[pos];
  30. if (degIn[pos] == 0) {
  31. que.push(pos);
  32. }
  33. }
  34. }
  35. for (int i = 0; i < n; ++i) {
  36. if (degIn[i] != 0) {
  37. return false;
  38. }
  39. }
  40. return true;
  41. }
  42. int main() {
  43. scanf("%d", &T);
  44. while (T--) {
  45. scanf("%d%d", &n, &m);
  46. memset(vis, -1, sizeof(vis));
  47. for (int i = 0; i < n; ++i) {
  48. for (int j = 0; j < m; ++j) {
  49. scanf("%d", &nums[id(i, j)]);
  50. }
  51. }
  52. for (int i = 0; i < m; ++i) {
  53. G[i].clear();
  54. degIn[i] = 0;
  55. }
  56. for (int i = 0; i < n; ++i) {
  57. for (int j = 0; j < m; ++j) {
  58. int x = nums[id(i, j)];
  59. if (x == 0) {
  60. continue;
  61. }
  62. vis[x] = i;
  63. idxOfNum[x] = j;
  64. }
  65. for (int j = 0; j < m; ++j) {
  66. int x = nums[id(i, j)];
  67. if (x == 0) {
  68. continue;
  69. }
  70. for (int k = x + x; k < maxn; k += x) {
  71. if (vis[k] == i) {
  72. G[idxOfNum[k]].push_back(j);
  73. ++degIn[j];
  74. }
  75. }
  76. }
  77. }
  78. if (!topsort(m)) {
  79. printf("no\n");
  80. continue;
  81. }
  82. for (int i = 0; i < n; ++i) {
  83. G[i].clear();
  84. degIn[i] = 0;
  85. }
  86. for (int j = 0; j < m; ++j) {
  87. int cnt = 0;
  88. for (int i = 0; i < n; ++i) {
  89. int x = nums[id(i, j)];
  90. if (x == 0) {
  91. continue;
  92. }
  93. numsTmp[cnt++] = x;
  94. idxOfNum[x] = i;
  95. }
  96. sort(numsTmp, numsTmp + cnt);
  97. for (int i = 1; i < cnt; ++i) {
  98. G[idxOfNum[numsTmp[i]]].push_back(idxOfNum[numsTmp[i - 1]]);
  99. ++degIn[idxOfNum[numsTmp[i - 1]]];
  100. }
  101. }
  102. if (!topsort(n)) {
  103. printf("no\n");
  104. continue;
  105. }
  106. printf("yes\n");
  107. }
  108. return 0;
  109. }

数据生成

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. const int maxn = 1000001;
  4. const int maxnm = 100000;
  5. const int testCases = 25;
  6. struct Set {
  7. public:
  8. set<int> st;
  9. vector<set<int>::iterator> vct;
  10. void init() {
  11. st.clear();
  12. vct.clear();
  13. }
  14. void add(int x) {
  15. pair<set<int>::iterator, bool> pr = st.insert(x);
  16. vct.push_back(pr.first);
  17. assert(st.size() == vct.size());
  18. }
  19. void del(int index) {
  20. assert(!st.empty());
  21. st.erase(vct[index]);
  22. vct[index] = vct.back();
  23. vct.pop_back();
  24. assert(st.size() == vct.size());
  25. }
  26. int size() {
  27. return vct.size();
  28. }
  29. };
  30. vector<int> multiple[maxn];
  31. ofstream fout;
  32. int T, n, m;
  33. Set st;
  34. int finalNums[maxnm], nums[maxnm], tmpNums[maxnm];
  35. int degIn[maxn], originDegIn[maxn];
  36. int rowIndex[maxnm], colIndex[maxnm];
  37. void initDAG() {
  38. for (int i = 1; i < maxn; ++i) {
  39. for (int j = i + i; j < maxn; j += i) {
  40. multiple[i].push_back(j);
  41. ++originDegIn[j];
  42. }
  43. }
  44. }
  45. string numToStr(int x) {
  46. string str;
  47. while (x != 0) {
  48. str = char('0' + x % 10) + str;
  49. x /= 10;
  50. }
  51. return str;
  52. }
  53. string getFileName(int index) {
  54. return "data/" + numToStr(index) + ".in";
  55. }
  56. int getRand(int l, int r) {
  57. return rand() % (r - l + 1) + l;
  58. }
  59. void initDegIn() {
  60. memcpy(degIn, originDegIn, sizeof(degIn));
  61. }
  62. inline int id(int x, int y) {
  63. return x * m + y;
  64. }
  65. void randomShuffle() {
  66. for (int i = 0; i < n; ++i) {
  67. rowIndex[i] = i;
  68. }
  69. random_shuffle(rowIndex, rowIndex + n);
  70. for (int j = 0; j < m; ++j) {
  71. colIndex[j] = j;
  72. }
  73. random_shuffle(colIndex, colIndex + m);
  74. for (int i = 0; i < n; ++i) {
  75. for (int j = 0; j < m; ++j) {
  76. finalNums[id(rowIndex[i], colIndex[j])] = nums[id(i, j)];
  77. }
  78. }
  79. }
  80. void genData() {
  81. if (getRand(0, 1) == 0) {
  82. // all in random, whether there is a solution or not
  83. int last = 0;
  84. for (int i = 0; i < n * m; ++i) {
  85. finalNums[i] = getRand(last + 1, (i + 1) * (maxn / maxnm));
  86. last = finalNums[i];
  87. }
  88. for (int i = 0; i < n * m / 3; ++i) {
  89. finalNums[getRand(0, n * m - 1)] = 0;
  90. }
  91. random_shuffle(finalNums, finalNums + n * m);
  92. return ;
  93. }
  94. st.init();
  95. st.add(1);
  96. initDegIn();
  97. for (int j = 0; j < m; ++j) {
  98. for (int i = 0; i < n; ++i) {
  99. int index = getRand(0, st.size() - 1);
  100. tmpNums[i] = *st.vct[index];
  101. st.del(index);
  102. for (auto mult: multiple[tmpNums[i]]) {
  103. --degIn[mult];
  104. if (degIn[mult] == 0) {
  105. st.add(mult);
  106. }
  107. }
  108. }
  109. sort(tmpNums, tmpNums + n);
  110. for (int i = 0; i < n; ++i) {
  111. nums[id(i, j)] = tmpNums[i];
  112. }
  113. }
  114. for (int i = 0; i < n * m / 2; ++i) {
  115. nums[getRand(0, n * m - 1)] = 0;
  116. }
  117. randomShuffle();
  118. }
  119. void writeToFile() {
  120. fout << n << " " << m << endl;
  121. for (int i = 0; i < n; ++i) {
  122. for (int j = 0; j < m; ++j) {
  123. if (j != 0) {
  124. fout << " ";
  125. }
  126. fout << finalNums[i * m + j];
  127. }
  128. fout << endl;
  129. }
  130. }
  131. void genDatas() {
  132. fout << T << endl;
  133. }
  134. int main() {
  135. srand(time(0));
  136. initDAG();
  137. // data in 20%, 1 <= T <= 10, 1 <= n,m <= 8
  138. int one = testCases * 20 / 100;
  139. for (int i = 1; i <= one; ++i) {
  140. fout.open(getFileName(i));
  141. T = 10;
  142. fout << T << endl;
  143. for (int j = 0; j < T; ++j) {
  144. n = 8;
  145. m = 8;
  146. genData();
  147. writeToFile();
  148. }
  149. fout.close();
  150. }
  151. // data in 80%, 1 <= T <= 10000, 1 <= n*m <= 100000
  152. int two = testCases * 80 / 100;
  153. for (int i = one + 1; i <= two; ++i) {
  154. fout.open(getFileName(i));
  155. T = 10;
  156. fout << T << endl;
  157. for (int j = 0; j < T; ++j) {
  158. n = getRand(100, 1000);
  159. m = getRand(maxnm / n - 10, maxnm / n);
  160. genData();
  161. writeToFile();
  162. }
  163. fout.close();
  164. }
  165. // data in last 20%, cornerCase
  166. for (int i = two + 1; i <= testCases; ++i) {
  167. fout.open(getFileName(i));
  168. T = 5;
  169. fout << T << endl;
  170. for (int j = 0; j < T; ++j) {
  171. switch (getRand(0, 2)) {
  172. case 0:
  173. n = 1;
  174. m = maxnm;
  175. break;
  176. case 1:
  177. n = maxnm;
  178. m = 1;
  179. break;
  180. case 2:
  181. n = getRand(2, 5);
  182. m = maxnm / n;
  183. break;
  184. }
  185. genData();
  186. writeToFile();
  187. }
  188. fout.close();
  189. }
  190. return 0;
  191. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注