@Dmaxiya
2022-12-04T14:23:20.000000Z
字数 6116
阅读 431
出题
通过观察可以发现行与列的操作互不影响,都不会改变两个元素在矩阵内同一行 / 列的相对关系,可以分别对行、列操作进行统计。
可以通过 暴力枚举所有可能的交换情况,然后对每种情况进行校验。
每行内若使用双重 for 循环校验时间复杂度为:,若检查每个数的倍数是否在当前行内时间复杂度为 ,由于全排列只能通过 的数据,因此使用 的最坏时间复杂度更小。
每列内只要记录上一次出现的非 值是否小于当前位置上的数字,就可以 次完成校验。
总时间复杂度为 ,可通过 的数据。
一、考虑到交换列时同一列内的元素相互绑定,在每一行内,必须保证一个数的约数在其之前,倍数在其之后,即某些列之间的前后关系是确定的,而另一些不能完全确定,最终需要将这些列按从 到 的顺序进行排序,若将每一列视为一个点,约数与倍数的相对关系就是一个有向图,判断能否在无限次交换内满足目标条件,可使用拓扑排序判断有向图内是否存在环,若存在环输出 no,不存在输出 yes。交换行的做法同理不再赘述。
二、如何快速找到有向边需要指向的数字,对于交换行而言,只需要将每列的数字取出排序,然后将相邻的两个非零数字连边即可。对于交换列而言,每行内的处理方式为:将所有数字放到一个集合中,跑到每一个正整数 时,判断其 倍是否存在于集合中,若存在则将两个数字所在列连一条有向边。
时间复杂度的计算:第一步拓扑排序时间复杂度是 ,第二步每列内排序时间复杂度是 ,每行内的最坏情况是 到 均匀分布在同一行,其时间复杂度约为:
#include <bits/stdc++.h>using namespace std;typedef long long LL;const int maxn = 100;int T, n, m;bool ans;int nums[maxn][maxn], idx[maxn];bool judgeRow() {for (int j = 0; j < m; ++j) {int last = 0;for (int i = 0; i < n; ++i) {if (nums[idx[i]][j] == 0) {continue;}if (nums[idx[i]][j] < last) {return false;}last = nums[idx[i]][j];}}return true;}bool judgeCol() {for (int i = 0; i < n; ++i) {for (int j = 0; j < m; ++j) {if (nums[i][idx[j]] == 0) {continue;}for (int k = j + 1; k < m; ++k) {if (nums[i][idx[k]] == 0) {continue;}if (nums[i][idx[j]] % nums[i][idx[k]] == 0) {return false;}}}}return true;}int main() {scanf("%d", &T);while (T--) {scanf("%d%d", &n, &m);for (int i = 0; i < n; ++i) {for (int j = 0; j < m; ++j) {scanf("%d", &nums[i][j]);}}for (int i = 0; i < n; ++i) {idx[i] = i;}ans = false;do {if (judgeRow()) {ans = true;break;}} while (next_permutation(idx, idx + n));if (!ans) {printf("no\n");continue;}for (int j = 0; j < m; ++j) {idx[j] = j;}ans = false;do {if (judgeCol()) {ans = true;break;}} while (next_permutation(idx, idx + m));if (ans) {printf("yes\n");} else {printf("no\n");}}return 0;}
#include <bits/stdc++.h>using namespace std;typedef long long LL;const int maxn = 1000000 + 100;const int maxnm = 100000 + 100;int T, n, m;bool ans;int nums[maxnm], degIn[maxnm], numsTmp[maxnm];int idxOfNum[maxn];int vis[maxn];vector<int> G[maxnm];queue<int> que;inline int id(int x, int y) {return x * m + y;}bool topsort(int n) {while (!que.empty()) {que.pop();}for (int i = 0; i < n; ++i) {if (degIn[i] == 0) {que.push(i);}}while (!que.empty()) {int tmp = que.front();que.pop();for (auto pos: G[tmp]) {--degIn[pos];if (degIn[pos] == 0) {que.push(pos);}}}for (int i = 0; i < n; ++i) {if (degIn[i] != 0) {return false;}}return true;}int main() {scanf("%d", &T);while (T--) {scanf("%d%d", &n, &m);memset(vis, -1, sizeof(vis));for (int i = 0; i < n; ++i) {for (int j = 0; j < m; ++j) {scanf("%d", &nums[id(i, j)]);}}for (int i = 0; i < m; ++i) {G[i].clear();degIn[i] = 0;}for (int i = 0; i < n; ++i) {for (int j = 0; j < m; ++j) {int x = nums[id(i, j)];if (x == 0) {continue;}vis[x] = i;idxOfNum[x] = j;}for (int j = 0; j < m; ++j) {int x = nums[id(i, j)];if (x == 0) {continue;}for (int k = x + x; k < maxn; k += x) {if (vis[k] == i) {G[idxOfNum[k]].push_back(j);++degIn[j];}}}}if (!topsort(m)) {printf("no\n");continue;}for (int i = 0; i < n; ++i) {G[i].clear();degIn[i] = 0;}for (int j = 0; j < m; ++j) {int cnt = 0;for (int i = 0; i < n; ++i) {int x = nums[id(i, j)];if (x == 0) {continue;}numsTmp[cnt++] = x;idxOfNum[x] = i;}sort(numsTmp, numsTmp + cnt);for (int i = 1; i < cnt; ++i) {G[idxOfNum[numsTmp[i]]].push_back(idxOfNum[numsTmp[i - 1]]);++degIn[idxOfNum[numsTmp[i - 1]]];}}if (!topsort(n)) {printf("no\n");continue;}printf("yes\n");}return 0;}
#include <bits/stdc++.h>using namespace std;const int maxn = 1000001;const int maxnm = 100000;const int testCases = 25;struct Set {public:set<int> st;vector<set<int>::iterator> vct;void init() {st.clear();vct.clear();}void add(int x) {pair<set<int>::iterator, bool> pr = st.insert(x);vct.push_back(pr.first);assert(st.size() == vct.size());}void del(int index) {assert(!st.empty());st.erase(vct[index]);vct[index] = vct.back();vct.pop_back();assert(st.size() == vct.size());}int size() {return vct.size();}};vector<int> multiple[maxn];ofstream fout;int T, n, m;Set st;int finalNums[maxnm], nums[maxnm], tmpNums[maxnm];int degIn[maxn], originDegIn[maxn];int rowIndex[maxnm], colIndex[maxnm];void initDAG() {for (int i = 1; i < maxn; ++i) {for (int j = i + i; j < maxn; j += i) {multiple[i].push_back(j);++originDegIn[j];}}}string numToStr(int x) {string str;while (x != 0) {str = char('0' + x % 10) + str;x /= 10;}return str;}string getFileName(int index) {return "data/" + numToStr(index) + ".in";}int getRand(int l, int r) {return rand() % (r - l + 1) + l;}void initDegIn() {memcpy(degIn, originDegIn, sizeof(degIn));}inline int id(int x, int y) {return x * m + y;}void randomShuffle() {for (int i = 0; i < n; ++i) {rowIndex[i] = i;}random_shuffle(rowIndex, rowIndex + n);for (int j = 0; j < m; ++j) {colIndex[j] = j;}random_shuffle(colIndex, colIndex + m);for (int i = 0; i < n; ++i) {for (int j = 0; j < m; ++j) {finalNums[id(rowIndex[i], colIndex[j])] = nums[id(i, j)];}}}void genData() {if (getRand(0, 1) == 0) {// all in random, whether there is a solution or notint last = 0;for (int i = 0; i < n * m; ++i) {finalNums[i] = getRand(last + 1, (i + 1) * (maxn / maxnm));last = finalNums[i];}for (int i = 0; i < n * m / 3; ++i) {finalNums[getRand(0, n * m - 1)] = 0;}random_shuffle(finalNums, finalNums + n * m);return ;}st.init();st.add(1);initDegIn();for (int j = 0; j < m; ++j) {for (int i = 0; i < n; ++i) {int index = getRand(0, st.size() - 1);tmpNums[i] = *st.vct[index];st.del(index);for (auto mult: multiple[tmpNums[i]]) {--degIn[mult];if (degIn[mult] == 0) {st.add(mult);}}}sort(tmpNums, tmpNums + n);for (int i = 0; i < n; ++i) {nums[id(i, j)] = tmpNums[i];}}for (int i = 0; i < n * m / 2; ++i) {nums[getRand(0, n * m - 1)] = 0;}randomShuffle();}void writeToFile() {fout << n << " " << m << endl;for (int i = 0; i < n; ++i) {for (int j = 0; j < m; ++j) {if (j != 0) {fout << " ";}fout << finalNums[i * m + j];}fout << endl;}}void genDatas() {fout << T << endl;}int main() {srand(time(0));initDAG();// data in 20%, 1 <= T <= 10, 1 <= n,m <= 8int one = testCases * 20 / 100;for (int i = 1; i <= one; ++i) {fout.open(getFileName(i));T = 10;fout << T << endl;for (int j = 0; j < T; ++j) {n = 8;m = 8;genData();writeToFile();}fout.close();}// data in 80%, 1 <= T <= 10000, 1 <= n*m <= 100000int two = testCases * 80 / 100;for (int i = one + 1; i <= two; ++i) {fout.open(getFileName(i));T = 10;fout << T << endl;for (int j = 0; j < T; ++j) {n = getRand(100, 1000);m = getRand(maxnm / n - 10, maxnm / n);genData();writeToFile();}fout.close();}// data in last 20%, cornerCasefor (int i = two + 1; i <= testCases; ++i) {fout.open(getFileName(i));T = 5;fout << T << endl;for (int j = 0; j < T; ++j) {switch (getRand(0, 2)) {case 0:n = 1;m = maxnm;break;case 1:n = maxnm;m = 1;break;case 2:n = getRand(2, 5);m = maxnm / n;break;}genData();writeToFile();}fout.close();}return 0;}