@Dmaxiya
2022-12-04T22:23:20.000000Z
字数 6116
阅读 237
出题
通过观察可以发现行与列的操作互不影响,都不会改变两个元素在矩阵内同一行 / 列的相对关系,可以分别对行、列操作进行统计。
可以通过 暴力枚举所有可能的交换情况,然后对每种情况进行校验。
每行内若使用双重 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 not
int 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 <= 8
int 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 <= 100000
int 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%, cornerCase
for (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;
}